using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using Amib.Threading;
using System.Net;
using System.IO;
using System.Data;
using System.Globalization;
using System.Xml;
using PipelineLib;
using System.Diagnostics;

namespace SmartFTPThread {
    public class SmartFTP {
        //Prova check in 
        //Lista dei contrattiID
        private List<ReportDaProcessare> reports;
        private SmartThreadPool smartThreadPool;
        private int maxActiveThreads;
        private int recordsPerThread;
        private int totalThreads;
        private string path;
        private string ambiente;
        private string cawTo_bin;
        private string cawToErrore;
        
        public SmartFTP(List<ReportDaProcessare> reportsDaProcessare, string path, string ambiente, string cawTo_Bin, string argomentiCawToErrore) {
            reports = reportsDaProcessare;
            maxActiveThreads = Convert.ToInt32(ConfigurationManager.AppSettings["maxActiveThreads"]);
            recordsPerThread = Convert.ToInt32(ConfigurationManager.AppSettings["recordsPerThread"]);
            totalThreads = (int)Math.Ceiling(reports.Count / (double)recordsPerThread);
            this.path = path;
            this.ambiente = ambiente;
            this.cawTo_bin = cawTo_Bin;
            this.cawToErrore = argomentiCawToErrore;
        }

        public void Start(int idZip)
        {
            #region COPIA I PDF DAL DB
            foreach (ReportDaProcessare reportDaProcessare in reports)
            {
                byte[] stream = null;
                List<Parametro> parametri = new List<Parametro>();
                Parametro p = new Parametro();

                p.ParameterName = "id";
                p.DbType = DbType.Int32;
                p.Value = reportDaProcessare.IdReport;
                parametri.Add(p);

                object report = DataAccess.ExecuteScalarStoredProcedure(DBProvider.SqlServer, "[dbo].[C6_S_readPDF]", parametri);

                if (report != null)
                    stream = (byte[])report;
                else
                {
                    throw new Exception("Errore nel file: " + reportDaProcessare.NomeFile + ", File non letto dal DB");
                    //continue;
                }
                //string path = ConfigurationManager.AppSettings["pathZIP"];
                FileStream fs = null;
                BinaryWriter bw = null;
                //try
                //{
                fs = File.Create(@path + "\\" + reportDaProcessare.NomeFile);
                bw = new BinaryWriter(fs);
                bw.Write(stream);

                p = new Parametro();
                p.ParameterName = "tipoReport";
                p.DbType = DbType.AnsiStringFixedLength;
                p.Value = reportDaProcessare.TipoReport;
                parametri.Add(p);


                p = new Parametro();
                p.ParameterName = "idZip";
                p.DbType = DbType.Int32;
                p.Value = idZip;
                parametri.Add(p);
                string query = ConfigurationManager.AppSettings["SP_GESTIONE_UPDATE_FTP"].ToString();
                int ritorno = DataAccess.ExecuteNonQueryStoredProcedure(DBProvider.SqlServerStampeC6, query, parametri);

                if (fs != null)
                    fs.Close();
                if (bw != null)
                    bw.Close();
            }
            #endregion



            //Commentato perch� non copiava tutti i report su disco
            #region INIZIALIZZAZIONE SMART_THREADPOOL

            //try {
            //    STPStartInfo stpStartInfo = new STPStartInfo();
            //    stpStartInfo.StartSuspended = true;
            //    stpStartInfo.MaxWorkerThreads = maxActiveThreads;
            //    stpStartInfo.MinWorkerThreads = 0;
            //    stpStartInfo.IdleTimeout = 10;
            //    smartThreadPool = new SmartThreadPool(stpStartInfo);
            //    smartThreadPool.Start();
            //    WIGStartInfo wigStartInfo = new WIGStartInfo();
            //    wigStartInfo.StartSuspended = true;

            //    Pipeline group = new Pipeline(smartThreadPool, int.MaxValue);


            //    //assegna ad ogni thread un sottoinsieme di contratti
            //    int j = 0;
            //    for (int i = 0; i < totalThreads; i++) {
            //        Worker worker;
            //        //smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoWork), CIDs);
            //        //counter += recordsPerThread;
            //        //if (counter >= reports.Count) counter = reports.Count - 1;
            //        if (j + recordsPerThread > reports.Count) {
            //            worker = new Worker(reports.GetRange(j, reports.Count - j), path, idZip);
            //            //smartThreadPool.QueueWorkItem(new WorkItemCallback(this.LocalCopy), reports.GetRange(j, reports.Count - j));
            //        }
            //        else {
            //            worker = new Worker(reports.GetRange(j, recordsPerThread), path, idZip);
            //            //smartThreadPool.QueueWorkItem(new WorkItemCallback(this.LocalCopy), reports.GetRange(j, recordsPerThread));
            //        }
            //        group.AddTask(worker);
            //        j += recordsPerThread;
            //    }
            #endregion 

            #region ESECUZIONE SMART E RILASCIO RISORSE
            //group.execute();
            //    if (group.inException) {
            //        //ScriviErroreNelDB(-292929, "Errore nello SmartFTP group.inException INIZIO", -292929, "SmartFTP", "SmartFTP");
            //        foreach (Task t in group.tasks) {
            //            if (t.taskData.innerException != null) {
            //                //Console.WriteLine(t.taskData.name + " eccezione=" + t.taskData.innerException.Message);
            //                try
            //                {
            //                    ScriviErroreNelDB(-292929, "Errore nello SmartFTP group.inException DENTRO: " + t.taskData.name + " __ " + getException(t.taskData.innerException), -292929, "SmartFTP", "SmartFTP"); 
            //                }
            //                catch
            //                {
            //                }
            //            }
            //        }
            //        //ScriviErroreNelDB(-292929, "Errore nello SmartFTP group.inException FINE", -292929, "SmartFTP", "SmartFTP");
            //        throw new Exception("Errore nello SmartFTP group.inException");
            //        //StartTng(ambiente, cawTo_bin, cawToErrore);

            //        //return 1;
            //    }
            //    //return 0;
            //}
            //catch (Exception e) {
            //    throw e;
            //}
            //finally {
            //    smartThreadPool.Shutdown();
            //}
            #endregion
        }


        private static void StartTng(string ambiente, string cawTo_Bin, string argomentiCaw) {
            if (!ambiente.Equals("SVILUPPO")) {
                Process processTng = new Process();
                try {
                    processTng = Process.Start(cawTo_Bin, argomentiCaw);
                    while (!processTng.HasExited) ;
                }
                catch (Exception ex) {
                    throw new Exception("cawTo_Bin: Non riesco ad eseguire " + cawTo_Bin + " sulla macchina " + System.Environment.MachineName + ":" + ex.Message);
                }
                finally {
                    processTng.Close();
                    processTng.Dispose();
                }
            }
        }

        private static void ScriviErroreNelDB(int codiceErrore, string descrizioErrore, int localeId, string nomePackage, string descrizionePackage) {
            int scriviErroreNelDB = int.Parse(ConfigurationManager.AppSettings["scriviErroreNelDB"]);
            if (scriviErroreNelDB == 1) {
                //try {
                    List<Parametro> parametri = new List<Parametro>();
                    Parametro parametro = new Parametro();
                    parametro.DbType = DbType.Int32;
                    parametro.ParameterName = "codiceErrore";
                    parametro.Value = codiceErrore;
                    parametri.Add(parametro);

                    parametro = new Parametro();
                    parametro.DbType = DbType.String;
                    parametro.ParameterName = "descrizioErrore";
                    parametro.Value = descrizioErrore;
                    parametri.Add(parametro);

                    parametro = new Parametro();
                    parametro.DbType = DbType.DateTime;
                    parametro.ParameterName = "dataTime";
                    parametro.Value = DateTime.Now;
                    parametri.Add(parametro);

                    parametro = new Parametro();
                    parametro.DbType = DbType.String;
                    parametro.ParameterName = "localeID";
                    if (localeId == 0)
                        parametro.Value = System.DBNull.Value;
                    else
                        parametro.Value = localeId;
                    parametri.Add(parametro);

                    parametro = new Parametro();
                    parametro.DbType = DbType.String;
                    parametro.ParameterName = "descrizionePackage";
                    parametro.Value = descrizionePackage;
                    parametri.Add(parametro);

                    parametro = new Parametro();
                    parametro.DbType = DbType.String;
                    parametro.ParameterName = "nomePackage";
                    parametro.Value = nomePackage;
                    parametri.Add(parametro);

                    DataAccess.ExecuteNonQueryStoredProcedure(DBProvider.SqlServerStampeC6, "[C6Mart].[UT_INSERT_ERROR]", parametri);
                //}
                //catch (Exception ex) {
                //    DataBaseException dataBaseException = (DataBaseException)ex;
                //    Console.WriteLine(dataBaseException.Message);
                //}
            }
        }

        private static string getException(Exception ecc) {
            const string aCapo = "#/n#";
            string ritorno = "";
            ritorno += aCapo;
            ritorno += "    tipo: " + ecc.GetType().ToString() + aCapo;
            ritorno += "    descrizione: " + ecc.Message + aCapo;
            ritorno += "    sorgente: " + ecc.Source + aCapo;
            if (ecc.InnerException != null) {
                ritorno += "    descrizione Inner: " + ecc.InnerException.Message + aCapo;
                ritorno += "    sorgente Inner: " + ecc.InnerException.Source + aCapo;
            }
            ritorno += "    traccia: " + FormattaErrore(ecc.StackTrace) + aCapo;
            return ritorno;

        }
        private static string FormattaErrore(string errore) {
            const string aCapo = "#/n#";
            const string spazio = "      ";
            string separatore1 = " at ";
            string separatore2 = " in ";
            string separatore3 = ":line ";
            string erroreInterno = errore.Replace(separatore1, "@");
            string ritorno = aCapo;


            //nome metodo e tutto il resto
            string[] messaggioErrore = erroreInterno.Split('@');
            string messaggioSenzaSpazi;
            string[] messaggioInterno;

            foreach (string messaggio in messaggioErrore) {
                messaggioSenzaSpazi = messaggio.Trim();

                if (messaggioSenzaSpazi != "") {
                    messaggioSenzaSpazi = messaggioSenzaSpazi.Replace(separatore2, "@");
                    messaggioInterno = messaggioSenzaSpazi.Split('@');
                    if (messaggioInterno.GetUpperBound(0) == 1) {
                        ritorno += spazio + "metodo: " + messaggioInterno[0] + aCapo;
                        string fileLineaSingolo = messaggioInterno[1].Replace(separatore3, "@");
                        string[] fileLinea = fileLineaSingolo.Split('@');
                        ritorno += spazio + "percorsoFile: " + fileLinea[0] + aCapo;
                        ritorno += spazio + "linea: " + fileLinea[1] + aCapo;
                        ritorno += aCapo;
                    }
                    else {
                        ritorno += spazio + "metodo: " + messaggioInterno[0] + aCapo;
                    }
                }
            }

            return ritorno;
        }

        public void StartAndWait() {
            smartThreadPool.Start();
            smartThreadPool.WaitForIdle();
            smartThreadPool.Shutdown();
        }
        //public object DoWork(int[] TCID) {
        #region CODICE DI PROVA IN LOCALE SENZA FTP
        private object LocalCopy(object t) {
            List<ReportDaProcessare> TCID = (List<ReportDaProcessare>)t;
            for (int i = 0; i < TCID.Count; i++) {
                ReportDaProcessare reportDaProcessare = TCID[i];
                byte[] stream = null;
                List<Parametro> parametri = new List<Parametro>();
                Parametro p = new Parametro();

                p.ParameterName = "id";
                p.DbType = DbType.Int32;
                p.Value = reportDaProcessare.IdReport;
                parametri.Add(p);

                object report = DataAccess.ExecuteScalarStoredProcedure(DBProvider.SqlServer, "[dbo].[C6_S_readPDF]", parametri);

                if (report != null)
                    stream = (byte[])report;
                else
                    continue;

                //string path = ConfigurationManager.AppSettings["pathZIP"];
                FileStream fs = null;
                BinaryWriter bw = null;
                try {
                    fs = File.Create(@path + "\\" + reportDaProcessare.NomeFile);
                    bw = new BinaryWriter(fs);
                    bw.Write(stream);
                    string query = ConfigurationManager.AppSettings["SP_GESTIONE_UPDATE_FTP"];
                    int ritorno = DataAccess.ExecuteNonQueryStoredProcedure(DBProvider.SqlServerStampeC6, query, parametri);
                }

                catch (Exception e) {
                    //Console.WriteLine(e.ToString());
                    throw e;

                }
                if (fs != null)
                    fs.Close();
                if (bw != null)
                    bw.Close();
            }

            return null;
        }

        #endregion CODICE DI PROVA IN LOCALE SENZA FTP





        #region CODICE THREAD FTP
        private object DoWork(object t) {
            List<ReportDaProcessare> TCID = (List<ReportDaProcessare>)t;
            try {
                for (int i = 0; i < TCID.Count; i++) {
                    ReportDaProcessare reportDaProcessare = TCID[i];
                    byte[] stream = null;
                    List<Parametro> parametri = new List<Parametro>();
                    Parametro p = new Parametro();

                    p.ParameterName = "id";
                    p.DbType = DbType.Int32;
                    p.Value = reportDaProcessare.IdReport;
                    parametri.Add(p);

                    object report = DataAccess.ExecuteScalarStoredProcedure(DBProvider.SqlServer, "[dbo].[C6_S_readPDF]", parametri);

                    if (report != null)
                        stream = (byte[])report;
                    else
                        continue;

                    FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ConfigurationManager.AppSettings["FTPServer"] + reportDaProcessare.NomeFile);

                    //request.Proxy = new WebProxy("155.208.255.162:8088");
                    request.Method = WebRequestMethods.Ftp.UploadFile;
                    request.ContentLength = stream.Length;
                    request.Credentials = new NetworkCredential(ConfigurationManager.AppSettings["FTPUser"], ConfigurationManager.AppSettings["FTPPassword"]);

                    byte[] buffer = new byte[4096];

                    using (Stream writer = request.GetRequestStream())
                    using (MemoryStream ms = new MemoryStream(stream)) {
                        int bytesRead = 0;
                        int totalBytes = 0;

                        do {
                            bytesRead = ms.Read(buffer, 0, buffer.Length);
                            if (bytesRead > 0) {
                                writer.Write(buffer, 0, bytesRead);
                                totalBytes += bytesRead;
                            }
                        } while (bytesRead > 0);
                        Console.WriteLine("Trasferiti {0} bytes per idReport {1} con nome file {2}", totalBytes, reportDaProcessare.IdReport, reportDaProcessare.NomeFile);

                        //Setto come trasferito il file
                        string query = ConfigurationManager.AppSettings["SP_GESTIONE_UPDATE_FTP"];
                        int ritorno = DataAccess.ExecuteNonQueryStoredProcedure(DBProvider.SqlServerStampeC6, query, parametri);
                    }

                    FtpWebResponse response = (FtpWebResponse)request.GetResponse();
                    //Console.WriteLine(response.StatusDescription + ", " + response.ResponseUri);
                    response.Close();
                    //e.Result = true;
                }
            }
            catch (WebException ex) {
                Console.WriteLine(((FtpWebResponse)ex.Response).StatusDescription);
                Console.WriteLine(ex.Message);
                throw ex;
                //e.Result = false;
            }
            catch (DataBaseException ex) {
                Console.WriteLine(ex.Message);
                if (ex.InnerException != null)
                    Console.WriteLine(ex.InnerException.Message);
                throw ex;
                //e.Result = false;
            }
            catch (Exception ex) {
                Console.WriteLine(ex.Message);
                if (ex.InnerException != null)
                    Console.WriteLine(ex.InnerException.Message);
                throw ex;
                //e.Result = false;
            }
            return null;
        }
        #endregion CODICE THREAD FTP

    }

    public class Worker : Task {

        private List<ReportDaProcessare> TCID;
        private string path;
        private int idZip;
        static int i = 0;
        public Worker(List<ReportDaProcessare> TCID, string path, int idZip)
        {
            this.TCID = TCID;
            this.path = path;
            this.idZip = idZip;
            this.taskData = new PipelineContext(i.ToString(), this);
            i++;
        }



        public override object Elaborate(Dictionary<String, Object> input) {

            for (int i = 0; i < TCID.Count; i++) {
                ReportDaProcessare reportDaProcessare = TCID[i];
                byte[] stream = null;
                List<Parametro> parametri = new List<Parametro>();
                Parametro p = new Parametro();

                p.ParameterName = "id";
                p.DbType = DbType.Int32;
                p.Value = reportDaProcessare.IdReport;
                parametri.Add(p);

                object report = DataAccess.ExecuteScalarStoredProcedure(DBProvider.SqlServer, "[dbo].[C6_S_readPDF]", parametri);

                if (report != null)
                    stream = (byte[])report;
                else
                    continue;

                //string path = ConfigurationManager.AppSettings["pathZIP"];
                FileStream fs = null;
                BinaryWriter bw = null;
                //try {

                    
                    fs = File.Create(@path + "\\" + reportDaProcessare.NomeFile);
                    bw = new BinaryWriter(fs);
                    bw.Write(stream);

                    p = new Parametro();
                    p.ParameterName = "tipoReport";
                    p.DbType = DbType.AnsiStringFixedLength;
                    p.Value = reportDaProcessare.TipoReport;
                    parametri.Add(p);


                    p = new Parametro();
                    p.ParameterName = "idZip";
                    p.DbType = DbType.Int32;
                    p.Value = idZip;
                    parametri.Add(p);
                    
                string query = ConfigurationManager.AppSettings["SP_GESTIONE_UPDATE_FTP"];
                int ritorno = DataAccess.ExecuteNonQueryStoredProcedure(DBProvider.SqlServerStampeC6, query, parametri);
                //}

                //catch (Exception e) {
                //    Console.WriteLine(e.ToString());
                //    throw new Exception(e.Message);

                //}
                if (fs != null)
                    fs.Close();
                if (bw != null)
                    bw.Close();
            }

            return null;
        }





    }


}