using System;
using System.Collections.Generic;
using Winbooks.TORM.OM;
using Winbooks.TORM.OM.Accounting;
using Winbooks.TORM.DAL;
using Winbooks.TORM.DAL.Accounting;
using Winbooks.TORM.OM.Logistics;
using Winbooks.TORM.DAL.Logistics;
using Winbooks.Apis.Services;
using Winbooks.Apis.Services.v2;
using Currency = Winbooks.TORM.OM.Currency;


namespace Winbooks.Apis.Demo
{
    public class Test
    {
        private static EntitiesServices _folder;

        public static string MainExecute(EntitiesServices folder)
        {
            _folder = folder;
            return ExecuteTest();
        }

        private static string ExecuteTest()
        {
            string ret = "";
            int i = 13;
            switch (i)
            {
                case 1: ret = GetVatId(); break;
                case 2: ret = GetCustomerNotLockedAndNotHidden(); break;
                case 3: ret = GetCodaTransactions(); break;
                case 4: ret = GetBookPeriodClosed(); break;
                case 5: ret = GetAddressId("BEBOP"); break;
                case 6: ret = GetOpenTransactions(); break;
                case 7: ret = GetGLTransactions(); break;
                case 8: ret = PostCustomerAndAddress("WB6"); break;
                case 9: ret = UpdateCustomerAndAddress("WB4"); break;
                case 10: ret = GetOpeningMiscOp(); break;
                case 11: ret = GetCustomerModifiedAfter(new DateTime(2018, 1, 1)); break;
                case 12: ret = GetDocumentsAfterNumber("INV", 20190024); break;
                case 13: ret = GetAllGLTransactions(1, 200); break;
                default: ret = "no choice " + i.ToString(); break;
            }
            return ret;
        }

        private static Guid GetId(ICriteria criteria, string type, string code)
        {
            Tools.Log(string.Format("Getting the {0} Id for code {1} ...", type, code), true);

            criteria.Add(Condition.Eq("Code", code))
                .SetProjection(Projections.Property("Id"))
                .SetMaxResults(1);
            Newtonsoft.Json.Linq.JArray id = _folder.GetFilterAll(criteria.JsonCriteriaSerialize(), type);
            Guid guid = id == null ? Guid.Empty : Guid.Parse(id[0].ToString());

            Tools.Log(" -> " + guid.ToString());

            return guid;
        }

        private static string GetVatId()
        {
            Tools.Log("Gettings the VAT id for code 21 ...", true);

            ICriteria criteria = new VatAccountDAO().CreateCriteria()
                .Add(Condition.Eq(VatAccount.Names.Code, "21"))
                .SetProjection(Projections.Property(VatAccount.Names.Id))
                .SetMaxResults(1);
            Newtonsoft.Json.Linq.JArray id = _folder.GetFilterAll(criteria.JsonCriteriaSerialize(), "VatAccount");

            Tools.Log("   Id = " + id[0].ToString());
            return "OK";
        }

        private static string GetCustomerNotLockedAndNotHidden()
        {
            Tools.Log("Gettings the customers not locked and not hidden ...", true);

            ICriteria criteria2 = new CustomerDAO().CreateCriteria()
            .Add(Condition.Eq(Customer.Names.IsLocked, false))
            .Add(Condition.Eq(Customer.Names.IsHidden, false));

            IList<Customer> customers = _folder.GetFilterAll<Customer>(criteria2);

            if (customers == null)
                Tools.Log("No customers");
            else
                Tools.Log(string.Format("{0} customers", customers.Count));

            Tools.Log(" => Done");
            return "OK";
        }

        private static string GetCustomerModifiedAfter(DateTime dateFrom)
        {
            Tools.Log(String.Format("Gettings the customers modified after {0} ...", dateFrom), true);

            ICriteria criteria = new CustomerDAO().CreateCriteria()
                .CreateAlias("third", typeof(Third), Third.Names.Id, Customer.Names.Third_Id)
                .Add(Condition.Eq("this." + Customer.Names.IsHidden, false))
                .Add(Condition.Or(
                    Condition.Ge("this." + Customer.Names.Modified, dateFrom),
                    Condition.Ge("this." + Customer.Names.Created, dateFrom)))
                 .SetProjection(Projections.Property("this." + Customer.Names.Code),
                                Projections.Property("third." + Third.Names.Name),
                                Projections.Property("this." + Customer.Names.Created),
                                Projections.Property("this." + Customer.Names.Modified));
            Newtonsoft.Json.Linq.JArray customers = _folder.GetFilterAll(criteria.JsonCriteriaSerialize(), "Customer");

            if (customers != null)
            {
                LogTable(new string[] { "Code", "Name", "Created", "Modified" }, new int[] { 10, 30, 18, 18 }, customers);
                Tools.Log(" => Done");
            }
            else
                Tools.Log(string.Format(" => no customers modified after {0}", dateFrom));

            return "OK";
        }

        private static string GetDocumentsAfterNumber(string jnlCode, int number)
        {
            Tools.Log(String.Format("Gettings the documents after number {0} ...", number), true);

            ICriteria criteria = new DocumentDetailDAO().CreateCriteria()
                .CreateAlias("header", typeof(DocumentHeader), DocumentHeader.Names.Id, DocumentDetail.Names.DocumentHeader_Id)
                .CreateAlias("row", typeof(DocumentRow), DocumentRow.Names.Id, DocumentDetail.Names.DocumentRow_Id)
                .CreateAlias("row", "item", typeof(Item), Item.Names.Id, DocumentRow.Names.Item_Id)
                .CreateAlias("header", "jnl", typeof(JournalLog), JournalLog.Names.Id, DocumentHeader.Names.JournalLog_Id)
                .Add(Condition.Eq("jnl." + JournalLog.Names.Code, jnlCode))
                .Add(Condition.Gt("header." + DocumentHeader.Names.Number, number))
                .SetProjection(Projections.Property("header." + DocumentHeader.Names.Number),
                               Projections.Property("header." + DocumentHeader.Names.Date),
                               Projections.Property("header." + DocumentHeader.Names.Reference),
                               Projections.Property("item." + Item.Names.Code),
                               Projections.Property("row." + DocumentRow.Names.Name),
                               Projections.Property("row." + DocumentRow.Names.UnitPrice),
                               Projections.Property("row." + DocumentRow.Names.DiscountRate),
                               Projections.Property("this." + DocumentDetail.Names.Quantity),
                               Projections.Property("this." + DocumentDetail.Names.Amount));
            Newtonsoft.Json.Linq.JArray details = _folder.GetFilterAll(criteria.JsonCriteriaSerialize(), "DocumentDetail");

            if (details != null)
            {
                LogTable(new string[] { "Number", "Date", "Reference", "ItemCode", "Name", "UP", "Disc", "Qty", "Amount" }, new int[] { 10, 15, 20, 20, 30, 10, 6, 6, 10 }, details);
                Tools.Log(" => Done");
            }
            else
                Tools.Log(string.Format(" => no {0} after {1}", jnlCode, number));

            return "OK";
        }

        public static void LogTable(string[] titles, int[] widths, Newtonsoft.Json.Linq.JArray data)
        {
            Tools.Log("");
            string title = "";
            string underline = "";
            for (int i = 0; i < titles.Length; i++)
            {
                if (titles[i].Length > widths[i])
                    title += titles[i].Substring(0, widths[i]) + " ";
                else
                    title += titles[i].PadRight(widths[i]) + " ";
                for (int j = 0; j < widths[i]; j++)
                    underline += "-";
                underline += " ";
            }
            Tools.Log(title);
            Tools.Log(underline);

            foreach (Newtonsoft.Json.Linq.JToken row in data)
            {
                string txt = "";
                for (int i = 0; i < titles.Length; i++)
                {
                    string s = row[i].ToString();
                    if (s.Length > widths[i])
                        txt += s.Substring(0, widths[i]) + " ";
                    else
                        txt += s.PadRight(widths[i]) + " ";
                }
                Tools.Log(txt);
            }
            Tools.Log("");
        }

        private static string GetCodaTransactions()
        {
            Tools.Log("Gettings the CodaTransaction linked to a GLTrans ...", true);

            Guid glTransId = new Guid("9248cdaf-800f-4b24-86ac-a50e01106f69");
            ICriteria criteria2 = new CodaTransactionDAO().CreateCriteria()
            .Add(Condition.Eq(CodaTransaction.Names.GLTrans_Id, glTransId));

            CodaTransaction codaTrans = _folder.GetFilter<CodaTransaction>(criteria2);

            Tools.Log(" => Done");
            return "OK";
        }

        private static string GetBookPeriodClosed()
        {
            Tools.Log("Gettings the closed BookPeriod ...", true);

            ICriteria criteria2 = new BookPeriodDAO().CreateCriteria()
            .Add(Condition.Eq(BookPeriod.Names.IsClosed, true));

            IList<BookPeriod> bookperiods = _folder.GetFilterAll<BookPeriod>(criteria2);

            Tools.Log(" => Done");
            return "OK";
        }

        private static string GetAddressId24() // This is the GetAddressId for version 2.4 and before
        {
            Tools.Log("Getting default address of BEBOP ...");
            /*
            ICriteria criteria = new AddressDAO().CreateCriteria()
                .CreateAlias("third", typeof(Third), Third.Names.Id, Address.Names.Third_Id)
                .Add(Condition.Eq("third." + Third.Names.Code, "BEBOP"))
                .Add(Condition.Eq(Address.Names.IsInvoicingDefault, true))
                .SetProjection(Projections.Property(Address.Names.Id));
            Newtonsoft.Json.Linq.JArray addressid = _folder.GetFilterAll(criteria.JsonCriteriaSerialize(), "Address");
            if (addressid == null)
                Tools.Log("  no address for BEBOP");
            else
                Tools.Log("   address_id = " + addressid[0].ToString());
            */
            return "OK";
        }

        private static string GetAddressId(string thirdCode)
        {
            Tools.Log("Getting default address of " + thirdCode + " ...", true);

            ICriteria criteria = new Third_AddressDAO().CreateCriteria()
                .CreateAlias("address", typeof(Address), Address.Names.Id, Third_Address.Names.Address_Id)
                .CreateAlias("third", typeof(Third), Third.Names.Id, Third_Address.Names.Third_Id)
                .Add(Condition.Eq("third." + Third.Names.Code, "BEBOP"))
                .Add(Condition.Eq(Third_Address.Names.IsInvoicingDefault, true))
                .SetProjection(Projections.Property("address." + Address.Names.Id));
            Newtonsoft.Json.Linq.JArray addressId = _folder.GetFilterAll(criteria.JsonCriteriaSerialize(), "Third_Address");
            if (addressId == null)
                Tools.Log("  => no address for " + thirdCode);
            else
            {
                Tools.Log(" => Address.Id = " + addressId[0].ToString());
            }
            return "OK";
        }

        private static string GetOpenTransactions()
        {
            Tools.Log("Getting open transactions of BEBOP", true);
            Customer cust = GetEntity.GetCustomer(_folder, "BILTHOVEN2");
            if (cust == null)
                return "BEBOP not found";

            Tools.Log(string.Format("Getting open transactions of BEBOP (customer.Id = {0}) by projection ...", cust.Id));

            ICriteria criteria = new GLTransDAO().CreateCriteria()
            .CreateAlias("header", typeof(GLTransHeader), GLTransHeader.Names.Id, GLTrans.Names.GLTransHeader_Id)
            .CreateAlias(JoinType.LeftOuterJoin, "match", typeof(GLMatch), GLMatch.Names.Id, GLTrans.Names.GLMatch_Id)
            .Add(Condition.Eq(GLTrans.Names.Customer_Id, cust.Id))
            .Add(Condition.Eq(GLTrans.Names.LineOrder, 0))
            .Add(Condition.Or(Condition.IsNull(GLTrans.Names.GLMatch_Id),
                              Condition.Eq("match." + GLMatch.Names.IsMatched, false)));

            criteria
                .CreateAlias("header", "jnl", typeof(Journal), Journal.Names.Id, GLTransHeader.Names.Journal_Id)
                .SetProjection(Projections.Property("jnl." + Journal.Names.Code),
                               Projections.Property("header." + GLTransHeader.Names.DocNumber),
                               Projections.Property("header." + GLTransHeader.Names.BookDate),
                               Projections.Property(GLTrans.Names.Amount),
                               Projections.Property(GLTrans.Names.CommentBooking),
                               Projections.Property(GLTrans.Names.CommentExternal));

            Newtonsoft.Json.Linq.JArray openTrans = _folder.GetFilterAll(criteria.JsonCriteriaSerialize(), "GLTrans");

            if (openTrans != null)
            {
                Tools.Log("JNL".PadRight(10) + " " + "Number".PadLeft(10) + " " + "Date".PadRight(10) + " " + "Amount".PadLeft(10) + " " + "Comment".PadRight(20));
                Tools.Log("---".PadRight(10) + " " + "------".PadLeft(10) + " " + "----".PadRight(10) + " " + "------".PadLeft(10) + " " + "-------".PadRight(20));
                foreach (Newtonsoft.Json.Linq.JToken trans in openTrans)
                    Tools.Log(trans[0].ToString().PadRight(10) + " " +
                              trans[1].ToString().PadLeft(10) + " " +
                              trans[2].ToString().Substring(0, 10).PadRight(10) + " " +
                              trans[3].ToString().PadLeft(10) + " " +
                              trans[4].ToString());
            }
            else
                Tools.Log(" => no open transaction");

            Tools.Log(" => OK");

            return "OK";
        }

        private static string GetAllGLTransactions(int firstResult = -1, int maxResult = -1)
        {

            Tools.Log(string.Format("Getting all transactions ..."), true);


            ICriteria criteria = new GLTransDAO().CreateCriteria()
            .CreateAlias("account", typeof(GLAccount), GLAccount.Names.Id, GLTrans.Names.GLAccount_Id)
            .CreateAlias("header", typeof(GLTransHeader), GLTransHeader.Names.Id, GLTrans.Names.GLTransHeader_Id)
            .CreateAlias("header", "jnl", typeof(Journal), Journal.Names.Id, GLTransHeader.Names.Journal_Id)
            .CreateAlias("header", "period", typeof(BookPeriod), BookPeriod.Names.Id, GLTransHeader.Names.BookPeriod_Id)
            .CreateAlias("period", "year", typeof(BookYear), BookYear.Names.Id, BookPeriod.Names.BookYear_Id)
            .CreateAlias(JoinType.LeftOuterJoin, "sup", typeof(Supplier), Supplier.Names.Id, GLTrans.Names.Supplier_Id)
            .SetProjection(Projections.Property("header." + GLTransHeader.Names.BookDate),
                           Projections.Property("year." + BookYear.Names.Name),
                           Projections.Property("period." + BookPeriod.Names.Name),
                           Projections.Property("jnl." + Journal.Names.Code),
                           Projections.Property("header." + GLTransHeader.Names.DocNumber),
                           Projections.Property("sup." + Supplier.Names.Code),
                           Projections.Property(GLTrans.Names.Amount),
                           Projections.Property(GLTrans.Names.CommentBooking),
                           Projections.Property(GLTrans.Names.CommentExternal))
            .Order("period." + BookPeriod.Names.RelatedYear)
            .Order("period." + BookPeriod.Names.RelatedMonth)
            .SetFirstResult(firstResult)
            .SetMaxResults(maxResult)
            ;

            Newtonsoft.Json.Linq.JArray transs = _folder.GetFilterAll(criteria.JsonCriteriaSerialize(), "GLTrans");

            if (transs != null)
            {
                foreach (Newtonsoft.Json.Linq.JToken trans in transs)
                    Tools.Log(trans[0].ToString() + " " +
                        trans[1].ToString() + " " +
                        trans[2].ToString() + " " +
                        trans[3].ToString() + " " +
                        trans[4].ToString() + " " +
                        trans[5].ToString() + " " +
                        trans[6].ToString() + " " +
                        trans[7].ToString() + " " +
                        trans[8].ToString()
                        );
            }
            else
                Tools.Log(" => no transaction");

            Tools.Log(" => OK");

            return "OK";
        }

        private static string GetGLTransactions()
        {
            string glAccountCode = "604000";
            DateTime dateFrom = new DateTime(2018, 1, 1);

            Tools.Log(string.Format("Getting transactions of {0} from {1} ...", glAccountCode, dateFrom), true);

            Guid glAccount_Id = GetId(new GLAccountDAO().CreateCriteria(), "GLAccount", glAccountCode);

            ICriteria criteria = new GLTransDAO().CreateCriteria()
            .CreateAlias("header", typeof(GLTransHeader), GLTransHeader.Names.Id, GLTrans.Names.GLTransHeader_Id)
            .CreateAlias("header", "jnl", typeof(Journal), Journal.Names.Id, GLTransHeader.Names.Journal_Id)
            .CreateAlias("header", "period", typeof(BookPeriod), BookPeriod.Names.Id, GLTransHeader.Names.BookPeriod_Id)
            .CreateAlias("period", "year", typeof(BookYear), BookYear.Names.Id, BookPeriod.Names.BookYear_Id)
            .CreateAlias(JoinType.LeftOuterJoin, "sup", typeof(Supplier), Supplier.Names.Id, GLTrans.Names.Supplier_Id)
            .Add(Condition.Eq(GLTrans.Names.GLAccount_Id, glAccount_Id))
            .Add(Condition.Ge("header." + GLTransHeader.Names.BookDate, dateFrom))
            .SetProjection(Projections.Property("header." + GLTransHeader.Names.BookDate),
                           Projections.Property("year." + BookYear.Names.Name),
                           Projections.Property("period." + BookPeriod.Names.Name),
                           Projections.Property("jnl." + Journal.Names.Code),
                           Projections.Property("header." + GLTransHeader.Names.DocNumber),
                           Projections.Property("sup." + Supplier.Names.Code),
                           Projections.Property(GLTrans.Names.Amount),
                           Projections.Property(GLTrans.Names.CommentBooking),
                           Projections.Property(GLTrans.Names.CommentExternal))
            .Order("header." + GLTransHeader.Names.BookDate)
            ;

            Newtonsoft.Json.Linq.JArray transs = _folder.GetFilterAll(criteria.JsonCriteriaSerialize(), "GLTrans");

            if (transs != null)
            {
                foreach (Newtonsoft.Json.Linq.JToken trans in transs)
                    Tools.Log(trans[0].ToString() + " " +
                        trans[1].ToString() + " " +
                        trans[2].ToString() + " " +
                        trans[3].ToString() + " " +
                        trans[4].ToString() + " " +
                        trans[5].ToString() + " " +
                        trans[6].ToString() + " " +
                        trans[7].ToString() + " " +
                        trans[8].ToString()
                        );
            }
            else
                Tools.Log(" => no transaction");

            Tools.Log(" => OK");

            return "OK";
        }

        private static string PostCustomerAndAddress(string custCode)
        {
            string msg = "Adding the customer " + custCode + " with an address ...";
            Tools.Log(msg, true);

            Customer cust = GetCustomer(custCode);
            Tools.Log("Posting the new customer and address");
            _folder.Insert<Customer>(cust, 5, false);

            Tools.Log(" => Done");
            return "OK";
        }

        private static string UpdateCustomerAndAddress(string custCode)
        {
            Tools.Log("Update the customer " + custCode + " with an address ...", true);

            Tools.Log("Get customer " + custCode);
            Customer cust = GetEntity.GetCustomer(_folder, custCode);
            if (cust == null)
                return "Customer " + custCode + " not found";
            if (cust.Third_Id == Guid.Empty)
                return "Third_Id of " + custCode + " is empty";

            Tools.Log("Get third " + cust.Third_Id.ToString());
            cust.Third = _folder.GetById<Third>(cust.Third_Id);
            if (cust.Third == null)
                return " => Third " + cust.Third_Id.ToString() + " of customer " + custCode + " not found";

            cust.Third.Name = cust.Third.Name + " .";
            Tools.Log("Update customer and third (level 2)");
            _folder.UpdateById<Customer>(cust, 2);

            Address address = GetEntity.GetDefaultAddressInvoicing(_folder, cust.Third_Id);
            if (address != null)
            {
                address.Address1 = address.Address1 + " .";
                Tools.Log("Update the default address ...");
                _folder.UpdateById<Address>(address);
            }
            Tools.Log(" => Done");

            return "OK";
        }

        private static Customer GetCustomer(string custCode)
        {
            Guid country_id = GetId(new CountryDAO().CreateCriteria(), "Country", "BE");
            Guid civility_id = GetId(new CivilityDAO().CreateCriteria(), "Civility", "NV");
            Guid language_id = GetId(new LanguageDAO().CreateCriteria(), "Language", "nl");
            Guid currency_id = GetId(new CurrencyDAO().CreateCriteria(), "Currency", "EUR");
            Guid vat_id = GetId(new VatDAO().CreateCriteria(), "Vat", "21");
            Guid customerCategory_id = GetId(new CustomerCategoryDAO().CreateCriteria(), "CustomerCategory", "BIG");
            Guid defaultAccount_Id = GetId(new GLAccountDAO().CreateCriteria(), "GLAccount", "700000");
            Guid centralAccount_Id = GetId(new GLAccountDAO().CreateCriteria(), "GLAccount", "400000");
            Guid payCode_id = GetId(new PayCodeDAO().CreateCriteria(), "PayCode", "7");

            Customer cust = new Customer()
            {
                Code = custCode,
                VatApplicable = VatApplicable.Subject,
                Vat_Id = vat_id,
                CustomerCategory_Id = customerCategory_id,
                GLAccount_Id = defaultAccount_Id,
                GLAccount2_Id = centralAccount_Id,
                PayCode_Id = payCode_id,
                Third = new Third()
                {
                    Code = custCode,
                    Name = "Customer " + custCode,
                    ThirdCivility_Id = civility_id,
                    Language_Id = language_id,
                    Currency_Id = currency_id,
                    VatCountry_Id = country_id,
                    VatNumber = "0000000394",
                    Addresses = new List<Third_Address>()
                }
            };

            Third_Address third_Address = new Third_Address()
            {
                Address = new Address()
                {
                    Address1 = "330 Lemonierlaan (" + custCode + ")",
                    Address2 = "",
                    PhoneNo = "+3223456789",
                    Country_Id = country_id,
                    Zip = "2000",
                    Town = "Antwerpen"
                },
                IsDelivering = true,
                IsDeliveringDefault = true,
                IsInvoicing = true,
                IsInvoicingDefault = true,
                IsPosting = true,
                IsPostingDefault = true
            };
            cust.Third.Addresses.Add(third_Address);

            return cust;
        }

        private static string GetOpeningMiscOp()
        {

            Tools.Log("Getting opening balance ...");

            ICriteria criteria = new GLTransDAO().CreateCriteria()
            .CreateAlias(JoinType.InnerJoin, "glaccount", typeof(GLAccount), GLAccount.Names.Id, GLTrans.Names.GLAccount_Id)
            .CreateAlias(JoinType.InnerJoin, "period", typeof(BookPeriod), Period.Names.Id, GLTrans.Names.BookPeriod_Id)
            .Add(Condition.Eq("period." + Period.Names.RelatedMonth, 0))
            .Add(Condition.Eq("period." + Period.Names.RelatedYear, 0))
            .SetProjection(Projections.Property("glaccount." + GLAccount.Names.Code),
                           Projections.Property(GLTrans.Names.Amount))
            ;

            Newtonsoft.Json.Linq.JArray openTrans = _folder.GetFilterAll(criteria.JsonCriteriaSerialize(), "GLTrans");

            if (openTrans != null)
                foreach (Newtonsoft.Json.Linq.JToken trans in openTrans)
                    Tools.Log(trans[0].ToString() + " " + trans[1].ToString());
            else
                Tools.Log("  no opening balance");

            Tools.Log("  Done");

            return "OK";
        }
    }

}
