/// <summary>
        /// Checks if the specified row exists in the database.
        /// If it does then the table will contain the row (as well as anything else that was in it
        /// before this method was called)
        /// </summary>
        /// <param name="ordersTable"></param>
        /// <param name="orderRow"></param>
        /// <param name="ID">The ID of the order that matches the supplied one</param>
        /// <returns></returns>
        public static bool Exists(EMMADataSet.OrdersDataTable ordersTable, EMMADataSet.OrdersRow orderRow,
            ref int ID, long corpID, long charID)
        {
            bool? exists = false;
            int? orderID = 0;
            EMMADataSet.OrdersDataTable tempTable = new EMMADataSet.OrdersDataTable();
            tableAdapter.ClearBeforeFill = false;

            lock (tableAdapter)
            {
                tableAdapter.FillOrderExists(tempTable, orderRow.OwnerID, orderRow.WalletID,
                    orderRow.StationID, orderRow.ItemID, orderRow.TotalVol, orderRow.RemainingVol,
                    orderRow.Range, orderRow.OrderState, orderRow.BuyOrder, orderRow.Price, orderRow.EveOrderID,
                    ref exists, ref orderID);
            }
            if (orderID.HasValue)
            {
                if (ordersTable.FindByID(orderID.Value) == null)
                {
                    ordersTable.ImportRow(tempTable.FindByID(orderID.Value));
                }
                else
                {
                    EMMADataSet.OrdersRow existingRow = ordersTable.FindByID(orderID.Value);
                    new EMMAException(ExceptionSeverity.Warning, "market order retreived in 'Exists' is not " +
                        "unique. diagnostics follow:" +
                        "\r\n\tCharacter: " + Names.GetName(charID) +
                        "\r\n\tCorporation: " + Names.GetName(corpID) +
                        "\r\n\tOrder ID: " + orderID.Value +
                        "\r\n\tLooking for this order:" +
                        "\r\n\t\tEve order ID: " + orderRow.EveOrderID +
                        "\r\n\t\tCorp order: " + orderRow.ForCorp.ToString() +
                        "\r\n\t\tStation: " + Stations.GetStationName(orderRow.StationID) +
                        "\r\n\t\tItem: " + Items.GetItemName(orderRow.ItemID) +
                        "\r\n\t\tType: " + (orderRow.BuyOrder ? "Buy" : "Sell") +
                        "\r\n\t\tTotal volume: " + orderRow.TotalVol +
                        "\r\n\t\tRemaining volume: " + orderRow.RemainingVol +
                        "\r\n\t\tPrice: " + orderRow.Price +
                        "\r\n\t\tStatus: " + OrderStates.GetStateDescription(orderRow.OrderState) +
                        "\r\n\tAlready have this order loaded:" +
                        "\r\n\t\tEve order ID: " + existingRow.EveOrderID +
                        "\r\n\t\tCorp order: " + existingRow.ForCorp.ToString() +
                        "\r\n\t\tStation: " + Stations.GetStationName(existingRow.StationID) +
                        "\r\n\t\tItem: " + Items.GetItemName(existingRow.ItemID) +
                        "\r\n\t\tType: " + (existingRow.BuyOrder ? "Buy" : "Sell") +
                        "\r\n\t\tTotal volume: " + existingRow.TotalVol +
                        "\r\n\t\tRemaining volume: " + existingRow.RemainingVol +
                        "\r\n\t\tPrice: " + existingRow.Price +
                        "\r\n\t\tStatus: " + OrderStates.GetStateDescription(existingRow.OrderState), true);
                }
            }

            ID = orderID.HasValue ? orderID.Value : 0;
            return exists.HasValue ? exists.Value : false;
        }
 public static bool GetJob(EMMADataSet.IndustryJobsDataTable table, long jobID)
 {
     _tableAdapter.ClearBeforeFill = false;
     lock (_tableAdapter)
     {
         _tableAdapter.FillByID(table, jobID);
     }
     EMMADataSet.IndustryJobsRow row = table.FindByID(jobID);
     return row != null;
 }
        /// <summary>
        /// Add the specified asset row to the specified data table
        /// </summary>
        /// <param name="assets"></param>
        /// <param name="ID"></param>
        /// <returns></returns>
        public static void AddAssetToTable(EMMADataSet.AssetsDataTable assets, long ID)
        {
            // Check if the row is already in the table
            EMMADataSet.AssetsRow row = assets.FindByID(ID);
            if (row == null)
            {
                lock (assetsTableAdapter)
                {
                    // If not then retrieve it from the database and place into the table.
                    bool previousClearBeforeFill = assetsTableAdapter.ClearBeforeFill;

                    assetsTableAdapter.ClearBeforeFill = false;
                    assetsTableAdapter.FillByID(assets, ID);

                    assetsTableAdapter.ClearBeforeFill = previousClearBeforeFill;
                }

            }
        }
 public static void AddTransByCalcProfitFromAssets(EMMADataSet.TransactionsDataTable trans,
     List<FinanceAccessParams> accessParams, int itemID, bool calcProfitFromAssets)
 {
     lock(tableAdapter)
     {
         bool oldClearBeforeFill = tableAdapter.ClearBeforeFill;
         tableAdapter.ClearBeforeFill = false;
         EMMADataSet.TransactionsDataTable tmpTable = new EMMADataSet.TransactionsDataTable();
         tableAdapter.FillByCalcProfitFromAssets(tmpTable, FinanceAccessParams.BuildAccessList(accessParams),
             itemID, calcProfitFromAssets);
         foreach (EMMADataSet.TransactionsRow tmpTrans in tmpTable)
         {
             EMMADataSet.TransactionsRow match = trans.FindByID(tmpTrans.ID);
             if (match == null)
             {
                 trans.ImportRow(tmpTrans);
             }
         }
         tableAdapter.ClearBeforeFill = oldClearBeforeFill;
     }
 }
        /// <summary>
        /// Recursive method to update the supplied assets data table based upon the supplied xml node list.
        /// </summary>
        /// <param name="assetData"></param>
        /// <param name="assetList"></param>
        /// <param name="locationID"></param>
        /// <param name="corc"></param>
        /// <param name="containerID"></param>
        /// <param name="expectedChanges"></param>
        private void UpdateAssets(EMMADataSet.AssetsDataTable assetData, XmlNodeList assetList, long locationID,
            CharOrCorp corc, long containerID, AssetList changes)
        {
            int counter = 0;
            if (containerID == 0)
            {
                UpdateStatus(counter, assetList.Count, "Getting asset data from file", "", false);
            }
            else
            {
                UpdateStatus(-1, -1, "Getting asset data from file", "", false,
                    counter, assetList.Count, "Container progress");
            }

            foreach (XmlNode asset in assetList)
            {
                int itemID;
                long assetID = 0, eveInstanceID, quantity;
                bool isContainer = false, needNewRow = false;

                XmlNode locationNode = asset.SelectSingleNode("@locationID");
                if (locationNode != null)
                {
                    locationID = long.Parse(locationNode.Value);

                    // Translate location ID from a corporate office to a station ID if required.
                    if (locationID >= 66000000 && locationID < 67000000)
                    {
                        // NPC station.
                        locationID -= 6000001;
                    }
                    if (locationID >= 67000000 && locationID < 68000000)
                    {
                        // Conquerable station.
                        locationID -= 6000000;
                    }
                }
                itemID = int.Parse(asset.SelectSingleNode("@typeID").Value,
                    System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
                eveInstanceID = long.Parse(asset.SelectSingleNode("@itemID").Value,
                    System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
                quantity = long.Parse(asset.SelectSingleNode("@quantity").Value,
                    System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
                if (asset.LastChild != null && asset.LastChild.Name.Equals("rowset"))
                {
                    isContainer = true;
                }

                EMMADataSet.AssetsRow assetRow;
                needNewRow = true;

                // Note that if a match is not found for the specific eve instance ID we pass in then
                // EMMA will automatically search for an asset matching all the other parameters.
                if (Assets.AssetExists(assetData, corc == CharOrCorp.Corp ? _corpID : _charID, locationID,
                    itemID, (int)AssetStatus.States.Normal, containerID != 0, containerID, isContainer,
                    false, !isContainer, false, true, eveInstanceID, ref assetID))
                {
                    needNewRow = false;
                }
                else if(!isContainer)
                {
                    // We havn't actually updated the database with anything yet so we may already have a
                    // matching item stack in memory but not in the database. Check for that here.
                    DataRow[] data =
                        assetData.Select("ItemID = " + itemID + " AND OwnerID = " + _charID + " AND CorpAsset = " +
                        (corc == CharOrCorp.Corp ? 1 : 0) + " AND LocationID = " + locationID +
                        " AND Status = " + (int)AssetStatus.States.Normal + " AND ContainerID = " + containerID +
                        " AND EveItemID = " + eveInstanceID);
                    if (data != null && data.Length > 0)
                    {
                        needNewRow = false;
                        assetID = ((EMMADataSet.AssetsRow)data[0]).ID;
                    }
                }

                Asset change = null;
                if (!needNewRow)
                {
                    assetRow = assetData.FindByID(assetID);

                    if (assetRow.Processed)
                    {
                        // Row is already in the database but has been processed so just add the current
                        // quantity to the row.
                        // (i.e. there are multiple stacks of the same item in the same location in-game
                        // EMMA merges these since we don't care how things are stacked and it makes
                        // things a little easier.)
                        assetRow.Quantity = assetRow.Quantity + quantity;
                        // We're stacking multiple eve item instances so just set the eve item ID to zero.
                        assetRow.EveItemID = 0;

                        // Store the changes that are being made to the quantity of
                        // items here.
                        // This means that once the update processing is complete, we
                        // can try and work out where these items came from.
                        #region Remember changes to item quantities
                        changes.ItemFilter = "ID = " + assetRow.ID;
                        if (changes.FiltredItems.Count > 0)
                        {
                            change = (Asset)changes.FiltredItems[0];
                            change.Quantity += quantity;
                            change.EveItemInstanceID = 0;
                            if (change.Quantity == 0) { changes.ItemFilter = ""; changes.Remove(change); }
                        }
                        else
                        {
                            change = new Asset(assetRow);
                            change.Quantity = quantity;
                            change.Processed = false;
                            changes.Add(change);
                        }
                        #endregion
                    }
                    else
                    {
                        if (assetRow.Quantity == quantity)
                        {
                            // The row already exists in the database and quantity is the same so
                            // set the processed flag on the database directly and remove the row
                            // from the dataset without setting it to be deleted when the database
                            // is updated.
                            // Note the processed flag MUST be set on the database for later routines
                            // to work correctly. (e.g. Assets.ProcessSellOrders)
                            if (assetRow.EveItemID != 0)
                            {
                                Assets.SetProcessedFlag(assetID, true);
                                assetData.RemoveAssetsRow(assetRow);
                            }
                            else
                            {
                                // If Eve instance ID is not yet set then set it.
                                Assets.SetProcessedFlag(assetID, true);
                                assetRow.Processed = true;
                                assetRow.EveItemID = eveInstanceID;
                            }
                        }
                        else if (assetRow.Quantity != quantity)
                        {
                            // The row already exists in the database, has not yet been processed
                            // and the quantity does not match what we've got from the XML.

                            // Store the changes that are being made to the quantity of
                            // items here.
                            // This means that once the update processing is complete, we
                            // can try and work out where these items came from.
                            #region Remember changes to item quantities
                            change = new Asset(assetRow);
                            change.Quantity = quantity - assetRow.Quantity;
                            change.EveItemInstanceID = eveInstanceID;
                            change.Processed = false;
                            changes.Add(change);
                            #endregion

                            // All we need to do is update the quantity and set the processed flag.
                            assetRow.Quantity = quantity;
                            assetRow.Processed = true;
                            assetRow.EveItemID = eveInstanceID;
                            // Also set the processed flag on the database directly. This will
                            // stop us from picking up this row later on (e.g. Assets.ProcessSellOrders)
                            Assets.SetProcessedFlag(assetID, true);
                        }
                    }
                }
                else
                {
                    // The row does not currently exist in the database so we need to create it.
                    assetRow = assetData.NewAssetsRow();
                    //assetRow.OwnerID = _charID;
                    assetRow.OwnerID = corc == CharOrCorp.Corp ? _corpID : _charID;
                    assetRow.CorpAsset = corc == CharOrCorp.Corp;
                    assetRow.ItemID = itemID;
                    assetRow.EveItemID = eveInstanceID;
                    assetRow.LocationID = locationID;
                    assetRow.Status = 1;
                    assetRow.Processed = true;
                    assetRow.AutoConExclude = false;
                    assetRow.ReprocExclude = false;
                    assetRow.Cost = 0;
                    assetRow.CostCalc = false;
                    assetRow.BoughtViaContract = false;

                    long systemID = 0, regionID = 0;
                    if (locationID >= 30000000 && locationID < 40000000)
                    {
                        systemID = locationID;
                        EveDataSet.mapSolarSystemsRow system = SolarSystems.GetSystem(locationID);
                        if (system != null)
                        {
                            regionID = system.regionID;
                        }
                        else
                        {
                            new EMMAEveAPIException(ExceptionSeverity.Warning, "Asset row added with unknown " +
                                "solar system ID (" + locationID + ")");
                        }
                    }
                    else
                    {
                        EveDataSet.staStationsRow station = null;
                        try
                        {
                            station = Stations.GetStation(locationID);
                        }
                        catch (EMMADataMissingException) { }

                        if (station != null)
                        {
                            systemID = station.solarSystemID;
                            regionID = station.regionID;
                        }
                        else
                        {
                            new EMMAEveAPIException(ExceptionSeverity.Warning, "Asset row added with unknown " +
                                "station ID (" + locationID + ")");
                        }
                    }
                    assetRow.SystemID = systemID;
                    assetRow.RegionID = regionID;
                    assetRow.Quantity = quantity;
                    assetRow.ContainerID = containerID;
                    assetRow.IsContainer = isContainer;
                    if (isContainer)
                    {
                        // If this asset is a container and has child assets then we must add it to the
                        // database now and get the correct ID number.
                        // (Because IDs are assigned by the database itself)
                        assetID = Assets.AddRowToDatabase(assetRow);
                    }
                    else
                    {
                        // Otherwise, we can just add it to the data table to be stored later along with
                        // everything else.
                        assetData.AddAssetsRow(assetRow);
                    }

                    // Store the changes that are being made to the quantity of
                    // items here.
                    // This means that once the update processing is complete, we
                    // can try and work out where these items came from.
                    #region Remember changes to item quantities
                    change = new Asset(assetRow);
                    if (isContainer) { change.ID = assetID; }
                    change.Quantity = quantity;
                    change.Processed = false;
                    changes.Add(change);
                    #endregion
                }

                if (isContainer)
                {
                    XmlNodeList contained = asset.SelectNodes("rowset/row");
                    UpdateAssets(assetData, contained, locationID, corc, assetID, changes);
                }

                counter++;
                if (containerID == 0)
                {
                    UpdateStatus(counter, assetList.Count, "Getting asset data from file", "", false);
                }
                else
                {
                    UpdateStatus(-1, -1, "Getting asset data from file", "", false,
                        counter, assetList.Count, "Container progress");
                }
            }
        }
        /// <summary>
        /// This is called after an import of XML asset data but before the changes are commited
        /// to the database.
        /// The aim is to try to work out which assets have simply moved location and which have 
        /// been added or lost. Additionally, if items have moved, update the cost of the item 
        /// stack appropriately.
        /// </summary>
        /// <param name="assetData">The datatable containing the changes that will be applied to the assets database</param>
        /// <param name="charID"></param>
        /// <param name="corp"></param>
        /// <param name="changes">Contains changes in quantities of item stacks that were in the XML.</param>
        /// <param name="gained">A list of items that have been gained from 'nowhere'</param>
        /// <param name="lost">A list of items that have been totally lost from the owner's asset data.</param>
        public static void AnalyseChanges(EMMADataSet.AssetsDataTable assetData, long ownerID,
            AssetList changes, out AssetList gained, out AssetList lost)
        {
            // Note that 'changes' will only include items that exist in the XML from the API. i.e. Any
            // stacks that have been completely removed from a location will not show up.
            // To get these removed assets, we need to retrieve any 'unprocessed' assets from the
            // assetData table.
            gained = new AssetList();
            lost = new AssetList();

            // Add any unprocessed asset stacks to the 'changes' collection.
            // Although the main data changes have not yet been supplied to the database,
            // the processed flags have been set for the relevant asset rows.
            // This means that we can get the list of unprocessed assets direct from
            // the database.
            List<AssetAccessParams> accessParams = new List<AssetAccessParams>();
            accessParams.Add(new AssetAccessParams(ownerID));
            EMMADataSet.AssetsDataTable unprocMatches = new EMMADataSet.AssetsDataTable();
            Assets.GetAssets(unprocMatches, accessParams, 0, 0, 0, 0, false);

            foreach (EMMADataSet.AssetsRow unprocMatch in unprocMatches)
            {
                Asset change = new Asset(unprocMatch);
                // Ignore any assets 'for sale via contract' or 'in transit'
                if (change.Quantity != 0 && change.StatusID != (int)AssetStatus.States.ForSaleViaContract &&
                    change.StatusID != (int)AssetStatus.States.InTransit)
                {
                    change.Quantity *= -1; // These assets are effectively missing so invert the quantity.
                    changes.Add(change);
                }
            }

            // Note 'changes2' is a list of the same items as 'changes'.
            // It is needed because the filter functionallity changes the list which we cannot
            // do within the main foreach loop.
            AssetList changes2 = new AssetList();
            foreach (Asset change in changes)
            {
                changes2.Add(change);
            }

            // See if any of our 'missing' assets match transactions that are marked to have thier profit
            // calculated during an assets update.
            #region Update transactions with CalcProfitFromAssets flag
            EMMADataSet.TransactionsDataTable transData = new EMMADataSet.TransactionsDataTable();
            Transactions.AddTransByCalcProfitFromAssets(transData,
                UserAccount.CurrentGroup.GetFinanceAccessParams(APIDataType.Transactions), 0, true);

            foreach (EMMADataSet.TransactionsRow trans in transData)
            {
                long quantityRemaining = trans.Quantity;
                decimal totalBuyPrice = 0;

                changes.ItemFilter = "ItemID = " + trans.ItemID + " AND Quantity < 0";
                foreach (Asset change in changes.FiltredItems)
                {
                    // If we get a match then use the missing items's cost to calculate the
                    // transaction's profit.
                    // Note that we don't need to adjust any item quantities since either the
                    // changes have already been made or the item stack is 'unprocessed' and will
                    // be cleared out anyway.
                    long deltaQ = Math.Min(Math.Abs(change.Quantity), quantityRemaining);
                    // Adjust the quantity of the 'missing' items.
                    change.Quantity += deltaQ;
                    quantityRemaining -= deltaQ;
                    // Because this transaction's asset quantity change will have been made
                    // back when the transaction was processed, we will have some unexpected
                    // added items as well.
                    //
                    // E.g. Consider station A and B. At A, there are 2 units of item X, at B
                    // there is nothing.
                    // At a later date, the player moves 2 of X from A to B and sells it.
                    // When the sell transaction is processed, station B will be set to -2
                    // units of X and the transaction will be set as 'CalcProfitFromAssets'.
                    // When the asset update occurs, it will show zero for both locations so
                    // they will be unprocessed.
                    // This will result in 2 unexplained units gained at station B and 2 lost
                    // at station A. (We've gone from A=2,B=-2 to A=0 B=0)
                    changes2.ItemFilter = "ItemID = " + trans.ItemID + " AND LocationID = " + trans.StationID +
                        " AND Status = 1";
                    if (changes2.FiltredItems.Count > 0)
                    {
                        // We've already accounted for the cost of items, etc so just reduce the
                        // quantity.
                        changes2[0].Quantity -= deltaQ;
                    }

                    totalBuyPrice += (change.UnitBuyPrice * deltaQ);

                    if (quantityRemaining != trans.Quantity)
                    {
                        // We've found enough missing items to match this transaction either completely
                        // or partially so calculate it's profit and set it as completed.
                        trans.CalcProfitFromAssets = false;
                        trans.SellerUnitProfit = trans.Price - (totalBuyPrice / trans.Quantity);
                    }
                }
            }
            /*
            foreach (Asset change in changes)
            {
                // Note: because we are changing quantity within the foreach loop
                // we don't use the filter on the AssetList.
                // Instead, just use this if condition each time around.
                if (change.Quantity < 0 && !noTransItemIDs.Contains(change.ItemID))
                {
                    // If we get a match then use the missing items's cost to calculate the
                    // transaction's profit.
                    // Note that we don't need to adjust any item quantities since either the
                    // changes have already been made or the item stack is 'unprocessed' and will
                    // be cleared out anyway.
                    Transactions.AddTransByCalcProfitFromAssets(transData,
                        UserAccount.CurrentGroup.GetFinanceAccessParams(APIDataType.Transactions),
                        change.ItemID, true);
                    if (transData.Count == 0) { noTransItemIDs.Add(change.ItemID); }
                    foreach (EMMADataSet.TransactionsRow trans in transData)
                    {
                        if (trans.ItemID == change.ItemID && !completedTrans.Contains(trans.ID))
                        {
                            long quantityRemaining = trans.Quantity;
                            TransProcessData data = new TransProcessData();
                            if (processData.ContainsKey(trans.ID))
                            {
                                data = processData[trans.ID];
                                quantityRemaining = data.QuantityRemaining;
                            }
                            else
                            {
                                data.QuantityMatched = 0;
                                data.QuantityRemaining = trans.Quantity;
                                data.TotalBuyPrice = 0;
                                processData.Add(trans.ID, data);
                            }

                            long deltaQ = Math.Min(Math.Abs(change.Quantity), quantityRemaining);
                            // Adjust the quantity of the 'missing' items.
                            change.Quantity += deltaQ;
                            // Because this transaction's asset quantity change will have been made
                            // back when the transaction was processed, we will have some unexpected
                            // added items as well.
                            //
                            // E.g. Consider station A and B. At A, there are 2 units of item X, at B
                            // there is nothing.
                            // At a later date, the player moves 2 of X from A to B and sells it.
                            // When the sell transaction is processed, station B will be set to -2
                            // units of X and the transaction will be set as 'CalcProfitFromAssets'.
                            // When the asset update occurs, it will show zero for both locations so
                            // they will be unprocessed.
                            // This will result in 2 unexplained units gained at station B and 2 lost
                            // at station A. (We've gone from A=2,B=-2 to A=0 B=0)
                            changes2.ItemFilter = "ItemID = " + trans.ItemID + " AND LocationID = " + trans.StationID;
                            foreach (Asset change2 in changes2)
                            {
                                if (change2.Quantity > 0)
                                {
                                    // We've already accounted for the cost of items, etc so just reduce the
                                    // quantity.
                                    change2.Quantity -= deltaQ;
                                }
                            }

                            data.QuantityRemaining = data.QuantityRemaining - deltaQ;
                            data.QuantityMatched = data.QuantityMatched + deltaQ;
                            data.TotalBuyPrice = data.TotalBuyPrice + (change.UnitBuyPrice * deltaQ);

                            if (data.QuantityRemaining == 0)
                            {
                                // We've found enough missing items to match this transaction completely
                                // so calculate it's profit and set it as completed.
                                trans.CalcProfitFromAssets = false;
                                trans.SellerUnitProfit = trans.Price - (data.TotalBuyPrice / data.QuantityMatched);
                                completedTrans.Add(trans.ID);
                                if (uncompletedTrans.Contains(trans.ID)) { uncompletedTrans.Remove(trans.ID); }
                            }
                            else
                            {
                                // We havn't found enough missing items to completely match this transaction
                                // yet so add to to the list of uncompleted transactions.
                                uncompletedTrans.Add(trans.ID);
                            }
                        }
                    }
                }
            }

            // Calculate profits as best we can for any 'uncompleted' transactions.
            // i.e. those that we did not have enough missing items to match completely.
            foreach (long transID in uncompletedTrans)
            {
                EMMADataSet.TransactionsRow trans = transData.FindByID(transID);
                if (trans != null && processData.ContainsKey(transID))
                {
                    TransProcessData data = processData[transID];
                    trans.CalcProfitFromAssets = false;
                    trans.SellerUnitProfit = trans.Price - (data.TotalBuyPrice / data.QuantityMatched);
                }
            }
             * */
            // Update transactions database
            Transactions.Store(transData);
            #endregion

            // Work through the list of changes.
            //
            // If items have been added then see if there is a matching quantity that has been removed
            // somwhere. If so, use the old asset stack cost to set the cost for the new asset stack.
            // If we can't find a match for any added items then add them to the 'gained' list.
            //
            // By definititon, any items that have been removed and match stacks added elsewhere will
            // be matched by the above process. Any removed items that are left over are added to the
            // 'lost' list of assets.
            changes.ItemFilter = "";

            // If we're in manufacturing mode then we don't want to try and match
            // lost assets with gained assets until after the user has selected
            // what has been built, etc.
            if (!UserAccount.Settings.ManufacturingMode)
            {
                #region Try and match gains in one place against losses in another.
                List<int> zeroLossItemIDs = new List<int>();
                foreach (Asset change in changes)
                {
                    if (change.Quantity > 0 && !zeroLossItemIDs.Contains(change.ItemID))
                    {
                        int itemID = change.ItemID;
                        long locationID = change.LocationID;

                        changes2.ItemFilter = "ItemID = " + itemID + " AND Quantity < 0";
                        if (changes2.FiltredItems.Count == 0)
                        {
                            if (!zeroLossItemIDs.Contains(change.ItemID)) { zeroLossItemIDs.Add(change.ItemID); }
                        }

                        foreach (Asset change2 in changes2.FiltredItems)
                        {
                            if (change.Quantity > 0 && change2.Quantity < 0)
                            {
                                // Get the asset data lines associated with the two changes in asset quantities
                                // that we have found.
                                //bool got1 = Assets.AssetExists(assetData, charID, corp, locationID, itemID,
                                //    change.StatusID, change.ContainerID != 0, change.ContainerID, change.IsContainer,
                                //    true, true, change.AutoConExclude, ref assetID1);
                                //bool got2 = Assets.AssetExists(assetData, charID, corp, change2.LocationID, itemID,
                                //    change2.StatusID, change2.ContainerID != 0, change2.ContainerID, change2.IsContainer,
                                //    true, true, change2.AutoConExclude, ref assetID2);
                                Assets.AddAssetToTable(assetData, change.ID);
                                Assets.AddAssetToTable(assetData, change2.ID);

                                EMMADataSet.AssetsRow row1 = assetData.FindByID(change.ID);
                                EMMADataSet.AssetsRow row2 = assetData.FindByID(change2.ID);
                                Asset a1 = new Asset(row1, null);
                                Asset a2 = new Asset(row2, null);
                                long thisAbsDeltaQ = Math.Min(Math.Abs(change.Quantity), Math.Abs(change2.Quantity));

                                // If the rows are processed then the actual movement of items has already happened
                                // so we don't need to adjust quantities.
                                // If they are not processed then we need to adjust the quantities now.
                                if (!row1.Processed) { row1.Quantity += thisAbsDeltaQ; }
                                if (!row2.Processed) { row2.Quantity -= thisAbsDeltaQ; }

                                // If the stack we're adding to now has zero items in it then we can just
                                // remove it. Otherwise we need to work out the value of the new stack.
                                if (row1.Quantity > 0)
                                {
                                    // Calculate the cost of items in the new stack based on the
                                    // cost of items from the old stack.
                                    decimal newCost = 0;
                                    newCost = ((a1.TotalBuyPrice) + (a2.UnitBuyPrice * thisAbsDeltaQ)) /
                                        (a1.Quantity + thisAbsDeltaQ);
                                    row1.Cost = newCost;
                                    row1.CostCalc = true;
                                }
                                else if (row1.Quantity == 0) { row1.Delete(); }
                                if (row2.Quantity == 0) { row2.Delete(); }

                                change.Quantity -= thisAbsDeltaQ;
                                change2.Quantity += thisAbsDeltaQ;
                            }
                        }

                    }
                }
                #endregion
            }

            // We have some items left over that could not be accounted for from losses/gains
            // elsewhere. Add these to the appropriate 'lost' or 'gained' list.
            #region Add remaining unexplained changes to the appropriate lost/gained list
            foreach (Asset change in changes)
            {
                if (change.Quantity != 0)
                {
                    Asset unexplained = new Asset();
                    unexplained.ID = change.ID;
                    unexplained.ItemID = change.ItemID;
                    unexplained.LocationID = change.LocationID;
                    unexplained.Quantity = change.Quantity;
                    unexplained.StatusID = change.StatusID;
                    unexplained.IsContainer = change.IsContainer;
                    unexplained.Container = change.Container;
                    unexplained.AutoConExclude = change.AutoConExclude;
                    unexplained.OwnerID = change.OwnerID;
                    if (change.UnitBuyPricePrecalculated)
                    {
                        unexplained.UnitBuyPrice = change.UnitBuyPrice;
                        unexplained.UnitBuyPricePrecalculated = true;
                    }
                    else
                    {
                        unexplained.UnitBuyPrice = 0;
                        unexplained.UnitBuyPricePrecalculated = false;
                    }
                    if (change.Quantity > 0)
                    {
                        // If the unexplained assets are for sale via contract or in transit then we
                        // would expect them not to show up if they are in the same state as before.
                        // This being the case, we do not need to add them to the list of unexplained items.
                        if (change.StatusID != (int)AssetStatus.States.ForSaleViaContract &&
                            change.StatusID != (int)AssetStatus.States.InTransit) { gained.Add(unexplained); }
                    }
                    else
                    {
                        // If the unexplained assets are for sale via contract or in transit then we
                        // would expect them not to show up if they are in the same state as before.
                        // This being the case, we do not need to add them to the list of unexplained items.
                        if (change.StatusID != (int)AssetStatus.States.ForSaleViaContract &&
                            change.StatusID != (int)AssetStatus.States.InTransit) { lost.Add(unexplained); }
                    }
                }
            }
            #endregion
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="table"></param>
        /// <param name="ownerID"></param>
        /// <param name="corpAsset"></param>
        /// <param name="locationID"></param>
        /// <param name="itemID"></param>
        /// <param name="containerID"></param>
        /// <param name="isContainer"></param>
        /// <param name="assetID"></param>
        /// <returns></returns>
        public static bool AssetExists(EMMADataSet.AssetsDataTable table, long ownerID,
            long locationID, int itemID, int status, bool isContained, long containerID, bool isContainer,
            bool processed, bool ignoreProcessed, bool autoConExclude, bool ignoreAutoConEx,
            long eveItemInstanceID, ref long assetID)
        {
            bool? exists = false;
            long? assetRowID = 0;

            lock (assetsTableAdapter)
            {
                // Have to be carefull here cos the row could well already exist in our table...
                EMMADataSet.AssetsDataTable tmpTable = new EMMADataSet.AssetsDataTable();
                assetsTableAdapter.FillAssetExists(tmpTable, ownerID, locationID, itemID, status,
                    isContained, containerID, isContainer, processed, ignoreProcessed, autoConExclude,
                    ignoreAutoConEx, eveItemInstanceID, ref exists, ref assetRowID);
                long id = assetRowID.HasValue ? assetRowID.Value : 0;
                EMMADataSet.AssetsRow row = table.FindByID(id);
                if (row == null && id != 0)
                {
                    EMMADataSet.AssetsRow dbrow = tmpTable.FindByID(id);
                    table.ImportRow(dbrow);
                }
            }

            assetID = assetRowID.HasValue ? assetRowID.Value : 0;
            return exists.HasValue ? exists.Value : false;
        }
        /// <summary>
        /// Update the assets table based on the transactions meeting the specified criteria.
        /// </summary>
        /// <param name="charID"></param>
        /// <param name="corpID"></param>
        /// <param name="useCorp"></param>
        /// <param name="minimumTransID"></param>
        /// <param name="includeTransAfter"></param>
        /// <returns></returns>
        private static long UpdateFromTransactions(EMMADataSet.AssetsDataTable assetsData,
            AssetList changes, long charID, long corpID, bool useCorp, long minimumTransID,
            DateTime includeTransAfter)
        {
            long maxID = 0;
            long ownerID = useCorp ? corpID : charID;

            EMMADataSet.TransactionsDataTable transactions = new EMMADataSet.TransactionsDataTable();
            transactions = GetTransactions(charID, corpID, useCorp, minimumTransID, includeTransAfter);

            foreach (EMMADataSet.TransactionsRow trans in transactions)
            {
                // If the ID is greater than 9000000000000000000 then it must have been created by EMMA as part of
                // an item exchange contract. These should be ignored here.
                if (trans.ID < 9000000000000000000)
                {
                    int deltaQuantity = trans.Quantity;
                    if (trans.SellerID == ownerID) { deltaQuantity *= -1; }

                    // Change this to not actually make the change in the database. Instead, use the
                    // asset data table passed in and record the changes made in the 'changes' list.

                    /*ChangeAssets(charID, useCorp, trans.StationID, trans.ItemID, 0, (int)AssetStatus.States.Normal,
                        false, deltaQuantity, (deltaQuantity > 0 ? trans.Price : 0), deltaQuantity > 0);*/
                    long assetID = 0;
                    EMMADataSet.AssetsRow asset = null;

                    AssetExists(assetsData, ownerID, trans.StationID, trans.ItemID,
                        (int)AssetStatus.States.Normal, false, 0, false, false, true, false, true, 0, ref assetID);

                    if (assetID != 0)
                    {
                        // Asset stack already exists in database and/or datatable, modify it
                        // based upon the transaction data.
                        asset = assetsData.FindByID(assetID);
                        asset.Quantity += deltaQuantity;
                        if (deltaQuantity > 0)
                        {
                            Asset logicalAsset = new Asset(asset);
                            asset.CostCalc = true;
                            asset.Cost = (logicalAsset.TotalBuyPrice + (trans.Price * trans.Quantity)) /
                                (logicalAsset.Quantity + trans.Quantity);
                        }
                        Asset chg = new Asset(asset);
                        chg.Quantity = deltaQuantity;
                        changes.Add(chg);
                    }
                    else
                    {
                        // Asset does not exist in database so add it to the datatable.
                        asset = assetsData.NewAssetsRow();
                        asset.Quantity = deltaQuantity;
                        asset.AutoConExclude = false;
                        asset.ContainerID = 0;
                        asset.CorpAsset = useCorp;
                        asset.Cost = trans.Price;
                        asset.CostCalc = true;
                        asset.IsContainer = false;
                        asset.ItemID = trans.ItemID;
                        asset.EveItemID = 0;
                        asset.LocationID = trans.StationID;
                        asset.OwnerID = charID;
                        asset.Processed = true;
                        asset.RegionID = trans.RegionID;
                        asset.ReprocExclude = false;
                        asset.Status = (int)AssetStatus.States.Normal;
                        asset.SystemID = Stations.GetStation(trans.StationID).solarSystemID;
                        asset.BoughtViaContract = false;
                        assetsData.AddAssetsRow(asset);
                        changes.Add(new Asset(asset));
                    }

                    if (trans.ID > maxID) { maxID = trans.ID; }
                }
            }

            return maxID;
        }
        /// <summary>
        /// This ensures that items in sell orders appear in the list of the player's assets.
        /// It is called just after new asset XML from the API has been processed but before
        /// the update is applied to the database.
        /// </summary>
        public static void ProcessSellOrders(EMMADataSet.AssetsDataTable assetData, AssetList changes,
            long ownerID)
        {
            List<int> itemIDs = new List<int>();
            itemIDs.Add(0);
            List<long> stationIDs = new List<long>();
            stationIDs.Add(0);
            List<AssetAccessParams> accessParams = new List<AssetAccessParams>();
            accessParams.Add(new AssetAccessParams(ownerID));
            // Get active sell orders
            OrdersList sellOrders = Orders.LoadOrders(accessParams, itemIDs, stationIDs, 999, "Sell");
            EMMADataSet.AssetsRow changedAsset = null;
            // Note that modifiedAssets is indexed first by itemID and second by stationID
            Dictionary<int, Dictionary<long, AssetInfo>> modifiedAssets = new Dictionary<int, Dictionary<long, AssetInfo>>();

            foreach (Order sellOrder in sellOrders)
            {
                bool foundMatch = false;
                long assetID = 0;

                // If there is already an asset row with a state of 'ForSaleViaMarket' in the same location,
                // and with the same item type then check quantity.
                // If it matches then just set to processed and move on.
                // If it does not then record the difference in quantities and go to the next order.
                // If we can't find a match then add a new asset row and record the items gained.
                if (Assets.AssetExists(assetData, ownerID, sellOrder.StationID, sellOrder.ItemID,
                    (int)AssetStatus.States.ForSaleViaMarket, false, 0, false, false, true, true,
                    true, 0, ref assetID))
                {
                    foundMatch = true;
                }
                else
                {
                    DataRow[] data =
                        assetData.Select("ItemID = " + sellOrder.ItemID + " AND OwnerID = " +
                        ownerID.ToString() + " AND LocationID = " + sellOrder.StationID +
                        " AND Status = " + (int)AssetStatus.States.ForSaleViaMarket);
                    if (data != null && data.Length > 0)
                    {
                        foundMatch = true;
                        assetID = ((EMMADataSet.AssetsRow)data[0]).ID;
                    }
                }

                if (foundMatch)
                {
                    changedAsset = assetData.FindByID(assetID);
                    if (changedAsset.Quantity != sellOrder.RemainingVol)
                    {
                        // If the quantities do not match then store how many units we are removing
                        // from the stack, the most likely cause is more than one sell order for
                        // this item in this location and if we know how many units we've removed
                        // We can make sure that the other order(s) quantity matches up.
                        Dictionary<long, AssetInfo> itemDeltaVol = new Dictionary<long, AssetInfo>();
                        if (modifiedAssets.ContainsKey(sellOrder.ItemID))
                        {
                            itemDeltaVol = modifiedAssets[sellOrder.ItemID];
                        }
                        else
                        {
                            modifiedAssets.Add(sellOrder.ItemID, itemDeltaVol);
                        }
                        if (itemDeltaVol.ContainsKey(sellOrder.StationID))
                        {
                            AssetInfo info = itemDeltaVol[sellOrder.StationID];
                            info.quantity += sellOrder.RemainingVol - changedAsset.Quantity;
                            itemDeltaVol[sellOrder.StationID] = info;
                            changedAsset.Quantity += sellOrder.RemainingVol;
                        }
                        else
                        {
                            AssetInfo info = new AssetInfo();
                            info.quantity = sellOrder.RemainingVol - changedAsset.Quantity;
                            info.assetID = changedAsset.ID;
                            itemDeltaVol.Add(sellOrder.StationID, info);
                            changedAsset.Quantity = sellOrder.RemainingVol;
                        }
                    }
                    changedAsset.Processed = true;
                    // Also set it to processed in the database.
                    SetProcessedFlag(changedAsset.ID, true);
                }

                // We havn't managed to match the order to an existing 'ForSaleViaMarket' stack in
                // the database or in memory.
                // As such, we need to create a new one.
                if (!foundMatch)
                {
                    // Create the new asset row..
                    changedAsset = assetData.NewAssetsRow();
                    changedAsset.AutoConExclude = true;
                    changedAsset.ContainerID = 0;
                    changedAsset.CorpAsset = false;
                    // Set cost to zero for now, it will be worked out later when gains/losses are reconciled.
                    changedAsset.Cost = 0;
                    changedAsset.CostCalc = false;
                    changedAsset.IsContainer = false;
                    changedAsset.ItemID = sellOrder.ItemID;
                    changedAsset.EveItemID = 0;
                    changedAsset.LocationID = sellOrder.StationID;
                    changedAsset.OwnerID = sellOrder.OwnerID;
                    changedAsset.Processed = true;
                    changedAsset.Quantity = sellOrder.RemainingVol;
                    changedAsset.RegionID = sellOrder.RegionID;
                    changedAsset.ReprocExclude = true;
                    changedAsset.SystemID = sellOrder.SystemID;
                    changedAsset.BoughtViaContract = false;
                    changedAsset.Status = (int)AssetStatus.States.ForSaleViaMarket;

                    assetData.AddAssetsRow(changedAsset);

                    // Store the changes we are making to quantities
                    Dictionary<long, AssetInfo> itemDeltaVol = new Dictionary<long, AssetInfo>();
                    if (modifiedAssets.ContainsKey(sellOrder.ItemID))
                    {
                        itemDeltaVol = modifiedAssets[sellOrder.ItemID];
                    }
                    else
                    {
                        modifiedAssets.Add(sellOrder.ItemID, itemDeltaVol);
                    }
                    if (itemDeltaVol.ContainsKey(sellOrder.StationID))
                    {
                        AssetInfo info = itemDeltaVol[sellOrder.StationID];
                        info.quantity += sellOrder.RemainingVol;
                        itemDeltaVol[sellOrder.StationID] = info;
                    }
                    else
                    {
                        AssetInfo info = new AssetInfo();
                        info.quantity = sellOrder.RemainingVol;
                        info.assetID = changedAsset.ID;
                        itemDeltaVol.Add(sellOrder.StationID, info);
                    }
                }

            }

            // Once we've finished processing all the orders, store the overall quantity changes.
            Dictionary<int, Dictionary<long, AssetInfo>>.Enumerator enumerator = modifiedAssets.GetEnumerator();
            while (enumerator.MoveNext())
            {
                Dictionary<long, AssetInfo>.Enumerator enumerator2 = enumerator.Current.Value.GetEnumerator();
                while(enumerator2.MoveNext())
                {
                    Asset change = new Asset();
                    change.ID = enumerator2.Current.Value.assetID;
                    change.ItemID = enumerator.Current.Key;
                    change.LocationID = enumerator2.Current.Key;
                    change.Quantity = enumerator2.Current.Value.quantity;
                    change.StatusID = (int)AssetStatus.States.ForSaleViaMarket;
                    change.IsContainer = false;
                    change.Container = null;
                    change.AutoConExclude = true;
                    change.OwnerID = ownerID;
                    change.UnitBuyPrice = 0;
                    change.UnitBuyPricePrecalculated = false;

                    changes.Add(change);
                }
            }
        }