using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace pmsDataGridView
{
    //most of the code is from 
    //http://www.dreamincode.net/forums/topic/44330-printing-in-c%23/
    public class PrintEngine : System.Drawing.Printing.PrintDocument
    {
        public enum PrintCommandType { NONE, LETTERFROM, LETTERTO, BEARINGLABEL, TOANDFROM, LABEL }

        #region Variables
        public static double mm2in = 3.9370078; //mm to a hundredth of inch
        public SaleLogList salelist = new SaleLogList();
        public CustomerList customerlist = new CustomerList();
        public PrintCommandType PrintCommand = PrintCommandType.NONE;

        //properties
        public string TextToPrint;
        public int quantity=0;
        public Font PrinterFont;
        //the indeces for the first available label, index starts from 1
        public int RowIndex = 1;
        public int ColIndex = 1;
        public const int Nrow = 8;
        public const int Ncol = 4;
        public int BearingIndexToPrint = 0;
        public int SaleIndexToPrint = 0;
        public int CustomerIndexToPrint = 0;  
        public bool HasMorePages = true;
        //address label sticker has 2 rows and 3 cols
        public int Add_RowIndex = 1;
        public int Add_ColIndex = 3;
        public const int Add_Nrow = 2;
        public const int Add_Ncol = 3;
        public bool[] FirstPageFlags = new bool[3] {false,false,false};
        public bool PrintingFirstPage = true;
        public int FirstLabelIndex = 3;
        #endregion
        //first, empty constructor
        //Since we are inheriting from a separate class, we need to call the Constructor of the base class. 
        //This is done by using base(), this will call the Constructor of our base class, the PrintDocument Class:
        public PrintEngine()
            : base()
        {
        }

        public PrintEngine(string label, int qty, PrintCommandType command) //the command is only limited to LABEL for now
            : base()
        {
            TextToPrint = label;
            quantity = qty;
            PrintCommand = command;
        }
        //non-empty constructor
        //this constructor are used to print address and labels
        public PrintEngine(SaleLogList s, CustomerList c, PrintCommandType command)
            : base()
        {
            salelist = s;
            customerlist = c;
            PrintCommand = command;            
        }

        #region Static Local Variables
        /// <summary>
        /// Static variable to hold the current character
        /// we're currently dealing with.
        /// </summary>        
        #endregion

        #region  OnPrintPage
        /// <summary>
        /// Override the default OnPrintPage method of the PrintDocument
        /// </summary>
        /// <param name=e></param>
        /// <remarks>This provides the print logic for our document</remarks>
        protected override void OnPrintPage(PrintPageEventArgs e)
        {
            if (PrintCommand == PrintCommandType.NONE) return;
            //load data from file
            load();

            // Run base code
            base.OnPrintPage(e);

            //Declare local variables needed
            StringBuilder sb = new StringBuilder();
            int printHeight = 1;
            int printWidth = 1;
            int pageheight = 1000;
            int pagewidth = 1000;
            int posX = 100;
            int posY = 100;
            int posX0 = (int)((205 - 202) * mm2in);
            int posY0 = (int)((205 - 16) * mm2in);

            //Use the StringFormat class for the text layout of our document
            //this variable 'format' is NOT supported in Linux
            StringFormat format = new StringFormat(StringFormatFlags.LineLimit);

            switch (PrintCommand)
            {
                default:
                case PrintCommandType.LETTERFROM:
                    PrinterFont = new Font("Verdana", 16);
                    pageheight = (int)(113 * mm2in);
                    pagewidth = (int)(225 * mm2in);
                    printHeight = (int)(70 * mm2in);
                    printWidth = (int)(150 * mm2in);
                    posX = (int)(130 * mm2in);
                    posY = (int)((205 - 57) * mm2in);
                    sb.Append("King Au\nNZ Miniature Bearings\n8 Karo Place\nIlam\nChristchurch 8041");
                    TextToPrint = sb.ToString();
                    PrintTextBox(posX, posY, pagewidth, pageheight, printWidth, printHeight, format, e);
                    break;
                case PrintCommandType.LETTERTO:
                    for (int j = 0; j < customerlist.Count; j++)
                    {
                        PrinterFont = new Font("Verdana", 16);
                        pageheight = (int)(113 * mm2in);
                        pagewidth = (int)(225 * mm2in);
                        printHeight = (int)(70 * mm2in);
                        printWidth = (int)(150 * mm2in);
                        posX = (int)(50 * mm2in);
                        posY = (int)((205 - 90) * mm2in);
                        sb.Append("Please deliver to:\n");
                        sb.Append(customerlist[j].Address + "\n" + customerlist[j].Country);
                        TextToPrint = sb.ToString();
                        PrintTextBox(posX, posY, pagewidth, pageheight, printWidth, printHeight, format, e);
                    }
                    break;
                case PrintCommandType.TOANDFROM: //for label of 3x2
                    //initialize for common parameters                    
                    pageheight = (int)(205 * mm2in);
                    pagewidth = (int)(162 * mm2in);
                    printHeight = (int)(50 * mm2in);
                    printWidth = (int)(84 * mm2in);
                    posX0 = (int)(5 * mm2in);
                    //posY0 = (int)(3 * mm2in);
                    /*
					int OFFSET = 5; // Offset to compensate for error in sachins printer
                    if (InitConfig.Business == InitConfig.BusinessType.BEARING)
                    {
                        //my printer can print the text higher on the label, so better utilise the space!
                        OFFSET = 0;
                    }
                     * */
                    int OFFSET = 0; 
					posY0 = (int)((3 * mm2in) + (OFFSET * mm2in) );

                    //calibrate
                    //PrinterFont = new Font("Verdana", 10);
                    //int x1 = 20,y1 = 100;
                    //int x2 = 40,y2 = 120;
                    //posX = (int)(x1 * mm2in);
                    //posY = (int)(y1 * mm2in);
                    //TextToPrint = x1.ToString() + y1.ToString();
                    //PrintTextBox(posX, posY, pagewidth, pageheight, printWidth, printHeight, format, e);
                    //posX = (int)(x2 * mm2in);
                    //posY = (int)(y2 * mm2in);
                    //TextToPrint = x2.ToString() + y2.ToString();
                    //PrintTextBox(posX, posY, pagewidth, pageheight, printWidth, printHeight, format, e);
                    
                    //search for the first label available
                    if (PrintingFirstPage)
                    {
                        for (int k = 2; k >= 0; k--)
                        {
                            if (FirstPageFlags[k] == true)
                            {
                                FirstLabelIndex = k + 1;
                                break;
                            }
                        }
                        //over write the index loaded from file
                        Add_ColIndex = FirstLabelIndex;
                    }
                    else
                    {
                        Add_ColIndex = 3;
                    }

                    for (int j = CustomerIndexToPrint; j < customerlist.Count; j++)
                    {
                        //My own address is left adjusted and customer's address is centred, so needs to set the format back to left adjusted again
                        format.Alignment = StringAlignment.Near;
                        format.LineAlignment = StringAlignment.Near;
                        //print "from" address
                        PrinterFont = new Font("Verdana", 14);
                        posX = posX0 + (int)(0 * 103 * mm2in)+ 10;//10 was added to make things look good on sachins LP
                        posY = posY0 + (int)((Add_ColIndex - 1) * 50 * mm2in) + 20;//15 was added to make things look good on sachins LP
                        if (InitConfig.Business == InitConfig.BusinessType.BEARING)
                        {
                            //TextToPrint = "From:\nKing Au\nNZ Miniature Bearings\n8 Karo Place\nIlam\nChristchurch 8041\nNew Zealand";
                            TextToPrint = "From:\nKing Au\nNZ Miniature Bearings\nPO box 36184\nMerivale\nChristchurch 8146\nNew Zealand";
                        }
                        else if (InitConfig.Business == InitConfig.BusinessType.ELECTRONICS)
                        {
                            TextToPrint = "From:\nHobbyist\n8 Karaka Place\nSomerfield\nChristchurch 8024\nNew Zealand"; 
                        }            
                        PrintTextBox(posX, posY, pagewidth, pageheight, printWidth, printHeight, format, e);
                        //print "to" address
                        PrinterFont = new Font("Verdana", 14);
                        format.Alignment = StringAlignment.Near;
                        format.LineAlignment = StringAlignment.Near;
                        posX = posX0 + (int)(1 * 103 * mm2in);
                        posY = posY0 + (int)((Add_ColIndex - 1) * 50 * mm2in) + 20;//15 was added to make things look good on sachins LP;
                        //TextToPrint = "Please deliver to:\n" + customer.Name + "\n" + customer.AddressLine1 + "\n" + customer.AddressLine2 + "\n" + customer.AddressLine3 + "\n" + customer.Country;
                        TextToPrint = "Please deliver to:\n" + customerlist[j].Name + "\n" + customerlist[j].Address + "\n" + customerlist[j].Country;
                        PrintTextBox(posX, posY, pagewidth, pageheight, printWidth, printHeight, format, e);
                        if (PrintingFirstPage)
                        {
                            while (Add_ColIndex > 1 && (FirstPageFlags[Add_ColIndex - 2] == false))
                            {//[Add_ColIndex - 2] because it is checking for the next one
                                //if the next label is empty                                
                                Add_ColIndex--;
                            }
                        }
                        // increment indices
                        if (Add_ColIndex > 1)
                        {
                            //count down from 3
                            Add_ColIndex--;
                        }
                        else
                        {
                            //reset row index to 3
                            Add_ColIndex = Add_Ncol;
                            //if more customer address to print
                            if (customerlist.Count > (j + 1))
                            {
                                //no longer printing on the first page
                                PrintingFirstPage = false;
                                //remember which which customer is due to print
                                CustomerIndexToPrint = j + 1;                               
                                save();
                                HasMorePages = true;
                                return;
                            }
                        }
                    }
                    //reset CustomerIndexToPrint so for the next print it starts from the first customer
                    CustomerIndexToPrint = 0;
                    HasMorePages = false;
                    break;
                case PrintCommandType.BEARINGLABEL:
                    PrinterFont = new Font("Verdana", 12);
                    pageheight = (int)(215 * mm2in);
                    pagewidth = (int)(165 * mm2in);
                    printHeight = (int)(25 * mm2in);
                    printWidth = (int)(38 * mm2in);
                    posX0 = (int)((205 - 202) * mm2in);
                    posY0 = (int)((205 - 16) * mm2in);
                    format.Alignment = StringAlignment.Center;
                    format.LineAlignment = StringAlignment.Center;
                    for (int j = SaleIndexToPrint; j < salelist.Count; j++)
                    {
                        for (int i = BearingIndexToPrint; i < salelist[j].BearingOrder.Count; i++)
                        {
                            Product b = salelist[j].BearingOrder[i];
                            posX = posX0 + (int)((ColIndex - 1) * 40 * mm2in);
                            posY = posY0 - (int)((RowIndex - 1) * 27 * mm2in);
                            TextToPrint = b.name + "\n" + b.Code + "\n" + b.quantity.ToString() + " pcs";
                            PrintTextBox(posX, posY, pagewidth, pageheight, printWidth, printHeight, format, e);

                            if (ColIndex < Ncol)
                            {
                                ColIndex++;
                            }
                            else
                            {
                                //reset the column index
                                ColIndex = 1;
                                //check if the end of row has reached
                                if (RowIndex < Nrow)
                                {
                                    RowIndex++;
                                }
                                else
                                {
                                    //reset row and col indices for a new page
                                    //Skip the first row as part of the print is missing
                                    RowIndex = 1; //uncomment this if want the 1st row to be printed
                                    //RowIndex = 2; //uncomment this if want the 2nd row to be printed
                                    ColIndex = 1;
                                    //if the existing customer has more bearings to print, remember the customer's index the due bearing index                           
                                    if (salelist[j].BearingOrder.Count > (i + 1))
                                    //if (salelist[j].BearingOrder.Count > (i + 1))
                                    {
                                        //remember which sale is due to print
                                        SaleIndexToPrint = j;

                                        //remember which bearing is due to print
                                        BearingIndexToPrint = i + 1;
                                        save();
                                        HasMorePages = true;
                                        return;
                                    }
                                    else if (salelist.Count > j + 1) //if there are more customers to print
                                    {
                                        //reset BearingIndexToPrint so for next sale print it starts from the first bearing
                                        BearingIndexToPrint = 0;
                                        //remember which sale is due to print
                                        SaleIndexToPrint = j+1; //from the next sale, cos print of sale(j) is completed
                                        save();
                                        HasMorePages = true;
                                        return; 
                                    }
                                }
                            }
                        }
                        //reset BearingIndexToPrint so for next sale print it starts from the first bearing
                        BearingIndexToPrint = 0;                     
                    }
                    //reset SaleIndexToPrint so for the next print it starts from the first sale
                    SaleIndexToPrint = 0;
                    HasMorePages = false;
                    break;

                case PrintCommandType.LABEL:
                    {
                        PrinterFont = new Font("Verdana", 16);
                        pageheight = (int)(215 * mm2in);
                        pagewidth = (int)(165 * mm2in);
                        printHeight = (int)(25 * mm2in);
                        printWidth = (int)(38 * mm2in);
                        posX0 = (int)((205 - 202) * mm2in);
                        posY0 = (int)((205 - 16) * mm2in);
                        format.Alignment = StringAlignment.Center;
                        format.LineAlignment = StringAlignment.Center;
                        for (int j = SaleIndexToPrint; j < quantity; j++)
                        {//the name SaleIndexToPrint doesn't fit the context here, just reuse the variable instead of creating a new one
                            posX = posX0 + (int)((ColIndex - 1) * 40 * mm2in);
                            posY = posY0 - (int)((RowIndex - 1) * 27 * mm2in);
                            PrintTextBox(posX, posY, pagewidth, pageheight, printWidth, printHeight, format, e);

                            if (ColIndex < Ncol)
                            {
                                ColIndex++;
                            }
                            else
                            {
                                //reset the column index
                                ColIndex = 1;
                                //check if the end of row has reached
                                if (RowIndex < Nrow)
                                {
                                    RowIndex++;
                                }
                                else
                                {
                                    //reset row and col indices for a new page
                                    //Skip the first row as part of the print is missing
                                    //RowIndex = 1; //uncomment this if want the 1st row to be printed
                                    RowIndex = 2;
                                    ColIndex = 1;
                                    if (quantity > j + 1) //if there are more labels to print
                                    {
                                        //remember which sale is due to print
                                        SaleIndexToPrint = j + 1; //from the next sale, cos print of sale(j) is completed
                                        save();
                                        HasMorePages = true;
                                        return;
                                    }
                                }
                            }
                        }

                        //reset SaleIndexToPrint so for the next print it starts from the first sale
                        SaleIndexToPrint = 0;
                        HasMorePages = false;
                        break;
                    }
            }
            save();
        }

        #endregion

        public void PrintTextBox(int posX, int posY, int pagewidth, int pageheight, int boxwidth, int boxheight, StringFormat format, PrintPageEventArgs e)
        {
            PaperSize CustomSize;
            CustomSize = new PaperSize("Custom Size", pagewidth, pageheight);
            e.PageSettings.PaperSize = CustomSize;
            //Create a rectangle printing are for our document
            RectangleF printArea = new RectangleF(posX, posY, boxwidth, boxheight);
            if (InitConfig.Business == InitConfig.BusinessType.ELECTRONICS)
            {
                //this variable 'textFormt' is supported in Linux
                StringFormat textFormat = new StringFormat(StringFormat.GenericTypographic);
                e.Graphics.DrawString(TextToPrint, PrinterFont, Brushes.Black, printArea, textFormat);
            }
            else
            {
                e.Graphics.DrawString(TextToPrint, PrinterFont, Brushes.Black, printArea, format);
            }
        }

        public bool[] get_firstpageflags()
        {
            update_firstpageflags();
            return FirstPageFlags;
        }
        public void update_firstpageflags()
        {
            //rest the flags to false
            for (int i = 0; i < 3; i++)
            {
                FirstPageFlags[i] = false;
            }
            //load the index from the file
            load();
            switch (Add_ColIndex)
            {
                case 3:
                    FirstPageFlags[2] = true;
                    goto case 2;
                case 2:
                    FirstPageFlags[1] = true;
                    goto case 1;
                case 1:
                    FirstPageFlags[0] = true;
                    break;                
            }
        }
        #region txt file read and write methods
        public void copy(PrintEngine sample)
        {
            this.salelist = sample.salelist;
            this.customerlist = sample.customerlist;
            this.PrintCommand = sample.PrintCommand;
            this.RowIndex = sample.RowIndex;
            this.ColIndex = sample.ColIndex;
            this.Add_RowIndex = sample.RowIndex;
            this.Add_ColIndex = sample.Add_ColIndex;
            this.BearingIndexToPrint = sample.BearingIndexToPrint;
            this.CustomerIndexToPrint = sample.CustomerIndexToPrint;
            this.SaleIndexToPrint = sample.SaleIndexToPrint;
            this.quantity = sample.quantity;
            this.TextToPrint = sample.TextToPrint;
        }
        public virtual void save()
        {
            //Pass the file path and file name to the StreamReader constructor
            StreamWriter sw = new StreamWriter(FilePath);
            sw.WriteLine(RowIndex.ToString());
            sw.WriteLine(ColIndex.ToString());
            sw.WriteLine(BearingIndexToPrint.ToString());
            sw.WriteLine(SaleIndexToPrint.ToString());
            sw.WriteLine(CustomerIndexToPrint.ToString());
            sw.WriteLine(Add_RowIndex.ToString());
            sw.WriteLine(Add_ColIndex.ToString());
            sw.Close();        
        }

        public virtual void load()
        {
            StreamReader sr = new StreamReader(FilePath);
            RowIndex = Convert.ToInt32(sr.ReadLine());
            ColIndex = Convert.ToInt32(sr.ReadLine());
            BearingIndexToPrint = Convert.ToInt32(sr.ReadLine());
            SaleIndexToPrint = Convert.ToInt32(sr.ReadLine());
            CustomerIndexToPrint = Convert.ToInt32(sr.ReadLine());
            Add_RowIndex = Convert.ToInt32(sr.ReadLine());
            Add_ColIndex = Convert.ToInt32(sr.ReadLine());
            sr.Close();
        }
        //private string FilePath = @"./../../ProgramData/PrinterData.txt";
        private string FilePath = @"./../../../../common/PrinterData.txt";
        #endregion
    }
}
