/*
	Rogue Trader.
	Copyright (c) 2007, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################
//
//
// Class Name:  GoodsInventory
// Purpose:     Class that describes all the types of goods available in the game.
//              At a singleton level, this class contains the number, names and
//              base prices for all the goods in the game. At an instance level
//              this class holds the amount of each type of good that is in the
//              inventory.
//
//
//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################

#include "GoodsInventory.h"

//------------------------------------------------------------------------------
// Constant initialisation
//------------------------------------------------------------------------------

// All of the different types of goods in the game and their normal prices

const GoodsInventory::ItemDescriptor GoodsInventory::ITEMS[GoodsInventory::NUM_ITEMS] =
{

    { "Foodstuffs"      , 25.0f     },
    { "Medicines"       , 50.0f     },
    { "Minerals"        , 75.0f     },
    { "Computers"       , 100.0f    },
    { "Robots"          , 125.0f    },
    { "Weaponry"        , 150.0f    },
    { "Nanites"         , 200.0f    },
    { "Jewellery"       , 300.0f    }

};

// Normal supply level for each good: price of good is at base price at this level of units available

const unsigned GoodsInventory::NORMAL_SUPPLY = 100;

// Affects how much under / over supply affects the price of each good

const float GoodsInventory::PRICE_ATTENUATION = 0.15f;

// How much of the buy price (in percent) goods can be sold for. This forms kind of a taxation on sales..

const float GoodsInventory::SALE_PRICE_PERCENT = 0.97f;

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GoodsInventory::GoodsInventory
Purpose:  Default constructor. Makes the amount of each item available in this
          inventory zero.
Params:   none
Returns:  n/a

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

GoodsInventory::GoodsInventory()
{
    // Set the amount of each item available to zero:

    for ( unsigned i = 0 ; i < NUM_ITEMS ; i++ ) itemCount[i] = 0;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GoodsInventory::GoodsInventory
Purpose:  Constructor. Randomnly selects how much of each item is in the inventory
          according to the random seed given.
Params:   Seed for the random number generator
Returns:  n/a

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

GoodsInventory::GoodsInventory( unsigned randomSeed )
{
    //--------------------------------------------------------------------------
    // Seed the random number generator first:
    //--------------------------------------------------------------------------

    mlRandomSeed(randomSeed);

    //--------------------------------------------------------------------------
    // Randomnly decide on much of each item there will be: at most there will
    // be twice the normal supply and at least there will be nothing:
    //--------------------------------------------------------------------------

    for ( unsigned i = 0 ; i < NUM_ITEMS ; i++ )
    {
        // Decide on item supply:

        itemCount[i] = mlRandomInt() % ( NORMAL_SUPPLY * 2 + 1 );
    }
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GoodsInventory::getItemName
Purpose:  Returns the name of an item, returns null if an invalid item is given
Params:   item number to get name for
Returns:  item name

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

const char * GoodsInventory::getItemName( unsigned itemNum )
{
    // If the an invalid item was specified return null:

    if ( itemNum >= NUM_ITEMS ) return 0;

    // Otherwise return the items name:

    return ITEMS[itemNum].name;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GoodsInventory::getItemAmount
Purpose:  Returns the amount of an item, returns -1 if an invalid item is given
Params:   item number to get name for
Returns:  item name

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

unsigned GoodsInventory::getItemAmount( unsigned itemNum )
{
    // If the an invalid item was specified return null:

    if ( itemNum >= NUM_ITEMS ) return (unsigned)(-1);

    // Otherwise return the amount of the item given:

    return itemCount[itemNum];
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GoodsInventory::getItemBuyPrice
Purpose:  Returns the buy price of an item at its current supply level in the inventory
Params:   item number to get price for
Returns:  price per unit of the item when buying

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

float GoodsInventory::getItemBuyPrice( unsigned itemNum )
{
    //--------------------------------------------------------------------------
    // If the an invalid item was specified return -1:
    //--------------------------------------------------------------------------

    if ( itemNum >= NUM_ITEMS ) return -1;

    //--------------------------------------------------------------------------
    // Save price here:
    //--------------------------------------------------------------------------

    float price = -1;

    //--------------------------------------------------------------------------
    // See what supply the item is at:
    //--------------------------------------------------------------------------

    if ( itemCount[itemNum] == NORMAL_SUPPLY )
    {
        //----------------------------------------------------------------------
        // Item quantity at normal supply: return base item price
        //----------------------------------------------------------------------

        price = ITEMS[itemNum].basePrice;
    }
    else if ( itemCount[itemNum] < NORMAL_SUPPLY )
    {
        //----------------------------------------------------------------------
        // Item understocked: calculate price
        //----------------------------------------------------------------------

        // Get base price first

        price = ITEMS[itemNum].basePrice;

        // Increase it according to the price attenuation factor:

        // price += ( float( NORMAL_SUPPLY - itemCount[itemNum] ) / float(NORMAL_SUPPLY) ) * PRICE_ATTENUATION * ITEMS[itemNum].basePrice;

        price *= 1.0f + ( float( NORMAL_SUPPLY - itemCount[itemNum] ) / float(NORMAL_SUPPLY) ) * PRICE_ATTENUATION;
    }
    else
    {
        //----------------------------------------------------------------------
        // Item overstocked: calculate price
        //----------------------------------------------------------------------

        // Get base price first

        price = ITEMS[itemNum].basePrice;

        // Decrease it according to the price attenuation factor:

        price /= powf( float(itemCount[itemNum]) / float(NORMAL_SUPPLY) , PRICE_ATTENUATION );
    }

    //--------------------------------------------------------------------------
    // Return item price:
    //--------------------------------------------------------------------------

    return mlDecimalRound(price,2);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GoodsInventory::getItemSellPrice
Purpose:  Returns the sell price of an item at its current supply level in the inventory
Params:   item number to get price for
Returns:  price per unit of the item when selling

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

float GoodsInventory::getItemSellPrice( unsigned itemNum )
{
    // Return the item buy price multiplied by the fraction of the buy price that sales are sold for:

    return mlDecimalRound( getItemBuyPrice(itemNum) * SALE_PRICE_PERCENT , 2 );
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GoodsInventory::setItemAmount
Purpose:  Sets the amount of an item available. Does nothing if an invalid item
          is given.
Params:   item number to set amount for, amount to set to
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GoodsInventory::setItemAmount( unsigned itemNum , unsigned amount )
{
    // Abort if item is invalid:

    if ( itemNum >= NUM_ITEMS ) return;

    // Otherwise set the item amount:

    itemCount[itemNum] = amount;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GoodsInventory::getTotalAmount
Purpose:  Tells how many units of goods there are in this inventory in total
Params:   none
Returns:  number of individual goods items there is in the inventory

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

unsigned GoodsInventory::getTotalAmount()
{
    // Store the total here:

    unsigned total = 0;

    // Add the quantity of each item to the total:

    for ( unsigned i = 0; i < NUM_ITEMS ; i++ ) total += itemCount[i];

    // Return the total:

    return total;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GoodsInventory::getTotalValue
Purpose:  Gets the total value of all the goods in this stock inventory. Uses
          base value of each item to calculate this and does not consider the
          shortage/surplus of items in the inventory when calculating price
Params:   none
Returns:  total value of all the goods in the inventory

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

float GoodsInventory::getTotalValue()
{
    // Save the total value here:

    float totalValue = 0;

    // Add the value of each good multiplied by its amount to the total value:

    for ( unsigned i = 0 ; i < NUM_ITEMS ; i++ )
    {
        // Add the value of this particular item to the total

        totalValue += ITEMS[i].basePrice * itemCount[i];
    }

    // Return the total value of all the goods:

    return totalValue;
}
