using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.IO;

using Siemens.Engineering;
using Siemens.Engineering.SW;
using Siemens.Engineering.SW.Blocks;
using Siemens.Engineering.HW;
using Siemens.Engineering.HW.Features;
using Siemens.Engineering.SW.ExternalSources;
using Siemens.Engineering.SW.Tags;


using Siemens.Engineering.Hmi;
using Siemens.Engineering.Hmi.Screen;
using Siemens.Engineering.Hmi.Tag;


using System.Xml;


namespace TIAOpennessGenerator
{
    class Generator
    {

        //Vyhledání zařízení v projektu
        /// <summary>
        /// Vyhledání zařízení v projektu
        /// </summary>
        /// <param name="project">Otevřený projekt TIA Portal</param>
        /// <param name="TypeDevice">Typ zařízení, PLC nebo HMI</param>
        /// <returns>Vráceno vyhledané zařízení</returns>
        private Device EnumerateDevicesInProject(Project project, string TypeDevice)
        {

            DeviceComposition deviceComposition = project.Devices;
            foreach (Device device in deviceComposition)
            {
                DeviceItemComposition deviceItemAggregation = device.DeviceItems;
                foreach (DeviceItem deviceItem in deviceItemAggregation)
                {

                    SoftwareContainer softwareContainer = deviceItem.GetService<SoftwareContainer>();
                    if (softwareContainer != null)
                    {
                        if (softwareContainer.Software is PlcSoftware && TypeDevice == "PLC")
                        {
                            PlcSoftware controllerTarget = softwareContainer.Software as PlcSoftware;
                            if (controllerTarget != null)
                            {
                                return device;

                            }
                        }
                        if (softwareContainer.Software is HmiTarget && TypeDevice == "HMI")
                        {
                            HmiTarget hmitarget = softwareContainer.Software as HmiTarget;
                            if (hmitarget != null)
                            {
                                return device;

                            }

                        }
                    }

                }


                /*
                if (device.Name == "S71500/ET200MP station_1" && TypeDevice == "PLC")      //PLC device                
                    return device;
                if (device.Name == "HMI_1" && TypeDevice == "HMI")                         //HMI device
                    return device;
                    */

            }

            return null;
        }

        // Returns PlcSoftware
        /// <summary>
        /// Vyhledání PLC softwaru
        /// </summary>
        /// <param name="project">Otevřený projekt TIA Portal</param>
        /// <returns>Vráceno vyhledaný PLC software</returns>
        private PlcSoftware GetPlcSoftware(Project project)
        {
            Device device = EnumerateDevicesInProject(project, "PLC");

            DeviceItemComposition deviceItemComposition = device.DeviceItems;
            foreach (DeviceItem deviceItem in deviceItemComposition)
            {
                SoftwareContainer softwareContainer = deviceItem.GetService<SoftwareContainer>();
                if (softwareContainer != null)
                {
                    Software softwareBase = softwareContainer.Software;
                    PlcSoftware plcSoftware = softwareBase as PlcSoftware;
                    return plcSoftware;
                }
            }
            return null;
        }



  
        /// <summary>
        /// Generuje celý PLC program, včetně tagů, a systemových datových bloků
        /// </summary>
        /// <param name="project">Otevřený projekt TIA Portal</param>
        public void GeneratePLC(Project project)
        {
            PlcSoftware plcSoftware = GetPlcSoftware(project);
            string path = Receipt.PathObject;
            //Pridání externalsource
            //Nejdříve je nutne přidat datové bloky a až posléze funkce, 
            //protože jinak se vyhodi vyjímka, protože nejsou známy použité tagy

            WriteTags(project);

            Receipt receipt = new Receipt();

            PlcExternalSource externalSource;

            List<Block> blocks = receipt.GetBlocks();

            foreach (Block item in blocks)
            {
                item.Process();
            }

            //Smazaní vsech external source
            while (plcSoftware.ExternalSourceGroup.ExternalSources.Count() > 0)
            {
                PlcExternalSource plcExternalSource = plcSoftware.ExternalSourceGroup.ExternalSources[0];
                plcExternalSource.Delete();
            }



            //Nahrani souboru do external source
            foreach (Block item in blocks)
                externalSource = plcSoftware.ExternalSourceGroup.ExternalSources.CreateFromFile(item.Name, path + item.FileName);


            //Přidání bloků z external source            
            foreach (PlcExternalSource plcExternalSource in plcSoftware.ExternalSourceGroup.ExternalSources)
            {
                plcExternalSource.GenerateBlocksFromSource();
            }

            ImportBlocks(plcSoftware, Receipt.PathObject + "Main.xml");
        }

        /// <summary>
        /// Import Bloku z XML souboru do projektu
        /// </summary>
        /// <param name="plcSoftware">PlcSoftware, do kterého má být importován blok</param>
        /// <param name="path">Cesta importovanému souboru + jeho jméno</param>
        private static void ImportBlocks(PlcSoftware plcSoftware, string path)
        {
            PlcBlockGroup blockGroup = plcSoftware.BlockGroup;
            IList<PlcBlock> blocks = blockGroup.Blocks.Import(new FileInfo(path), ImportOptions.Override);
        }


        ////Enumerates all plc tag tables in a specific system group or and user group
        //private static void DeleteTagTable(PlcSoftware plcSoftware)
        //{
        //    PlcTagTableComposition tagTables = plcSoftware.TagTableGroup.TagTables;
        //    // alternatively, PlcTagTableComposition tagTables =
        //    //plcSoftware.TagTableGroup.Groups.Find("UserGroup XYZ").TagTables;
        //    foreach (PlcTagTable tagTable in tagTables)
        //    {
        //        if (tagTable.Name == "TagTable")
        //        {
        //            tagTable.Delete();
        //            break;
        //        }

        //    }
        //}

        /// <summary>
        /// Vytvoření tabulky tagů se všemi tagy obsažených v XML předpisu
        /// </summary>
        /// <param name="project">Otevřený projekt TIA Portal</param>
        private void WriteTags(Project project)
        {
            PlcSoftware plcSoftware = GetPlcSoftware(project);

            Receipt receipt = new Receipt();

            XmlNode TagTable = receipt.GetTagList();
            string TableName = TagTable.Attributes["Name"].Value;

            XmlNodeList Tags = TagTable.ChildNodes;

            //Pokud existuje TagTable s daným jménem smaž ji
            PlcTagTableComposition tagTables = plcSoftware.TagTableGroup.TagTables;
            foreach (PlcTagTable tagTable in tagTables)
            {
                if (tagTable.Name == TableName)
                {
                    tagTable.Delete();
                    break;
                }

            }

            //DeleteTagTable(plcSoftware);

            //Vytvoření tabulky tagů
            PlcTagTable plcTagTable = plcSoftware.TagTableGroup.TagTables.Create(TableName);

            PlcTagTable table = plcSoftware.TagTableGroup.TagTables.Find(TableName);
            PlcTagComposition tagComposition = table.Tags;
            foreach (XmlNode Tag in Tags)
            {
                PlcTag tag = tagComposition.Create(Tag.InnerText,
                    Tag.Attributes["Type"].Value, Tag.Attributes["Adress"].Value);
            }
        }



        /// <summary>
        /// Vyhledání HMI softwaru
        /// </summary>
        /// <param name="project">Otevřený projekt TIA Portal</param>
        /// <returns>Vráceno vyhledaný HMI software</returns>
        private HmiTarget GetHmiTarget(Project project)
        {
            Device device = EnumerateDevicesInProject(project, "HMI");

            DeviceItemComposition deviceItemComposition = device.DeviceItems;
            foreach (DeviceItem deviceItem in deviceItemComposition)
            {
                SoftwareContainer softwareContainer = deviceItem.GetService<SoftwareContainer>();
                if (softwareContainer != null)
                {
                    Software softwareBase = softwareContainer.Software;
                    HmiTarget hmiTarget = softwareBase as HmiTarget;
                    return hmiTarget;
                }
            }
            return null;
        }

        //Verze 1
        /*
        public void GenerateHMI(Project project)
        {
            HmiTarget hmiTarget = GetHmiTarget(project);

            //Importy připravenych souboru
            Receipt receipt = new Receipt();

            List<HMI> HMIs = receipt.GetHMIs();

            string path;

            foreach (HMI item in HMIs)
            {
                item.Process();
                path = Receipt.PathObject + item.ScreenFile;
                ImportSingleScreen(hmiTarget, path);

                path = Receipt.PathObject + item.TagTableFile;
                ImportSingleHMITagTable(hmiTarget, path);
            }

        }
        */

        //Verze 2
        /// <summary>
        /// Generování HMI, Screenů a tabulky tagů
        /// </summary>
        /// <param name="project">Otevřený projekt TIA Portal</param>
        public void GenerateHMI(Project project)
        {
            HmiTarget hmiTarget = GetHmiTarget(project);

            //Importy připravenych souboru
            Receipt receipt = new Receipt();

            List<HMI> HMIs = receipt.GetHMIs();

            string path;
            path = WriteHMITagTable();
            
            ImportSingleHMITagTable(hmiTarget, Receipt.PathObject + path);

            foreach (HMI item in HMIs)
            {
                item.Process();
                path = Receipt.PathObject + item.ScreenFile;
                ImportSingleScreen(hmiTarget, path);
            }

        }

        /// <summary>
        /// Vytvoří HMI TagTable s všemi HMI tagy dle XML přepisu
        /// </summary>
        /// <returns>Vrátí název xml souboru obsahující TagTable</returns>
        private string WriteHMITagTable()
        {
            XmlNode TagList;
            String FileNameTagTable;
            int number = 1;

            Receipt receipt = new Receipt();

            TagList = receipt.GetHMITagList();
            FileNameTagTable = TagList.Attributes["Name"].Value + ".xml";

            XmlDocument FileTag = new XmlDocument();
            FileTag.Load(Receipt.PathSource + "SingleTag.xml");
            XmlDocument FileTagTable = new XmlDocument();
            FileTagTable.Load(Receipt.PathSource + "TagTable.xml");

            XmlNodeList Tags = TagList.ChildNodes;
            XmlNode TagTable = FileTagTable.SelectSingleNode("//Hmi.Tag.TagTable/ObjectList");
            XmlNode SingleTag = FileTag.SelectSingleNode("//Hmi.Tag.Tag");

            foreach (XmlNode Tag in Tags)
            {
                FileTag.Load(Receipt.PathSource + "SingleTag.xml");
                SingleTag = FileTag.SelectSingleNode("//Hmi.Tag.Tag");
                SingleTag.Attributes["ID"].Value = number.ToString();

                SingleTag = SingleTag.SelectSingleNode("AttributeList/Name");
                SingleTag.InnerText = Tag.InnerText;
                SingleTag = FileTag.SelectSingleNode("//LinkList/ControllerTag/Name");
                SingleTag.InnerText = Tag.Attributes["PLCTag"].Value;
                SingleTag = FileTag.SelectSingleNode("//LinkList/DataType/Name");
                SingleTag.InnerText = Tag.Attributes["Type"].Value;
                SingleTag = FileTag.SelectSingleNode("//LinkList/HmiDataType/Name");
                SingleTag.InnerText = Tag.Attributes["Type"].Value;
                if(Tag.Attributes["Type"].Value=="Int")
                {
                    SingleTag = FileTag.SelectSingleNode("//Hmi.Tag.Tag/AttributeList/Length");
                    SingleTag.InnerText = "2";
                }

                XmlNodeList IDs = SingleTag.SelectNodes("//MultilingualText");

                foreach (XmlNode ID in IDs)
                {
                    ID.Attributes["ID"].Value = number.ToString() + ID.Attributes["ID"].Value;
                }

                IDs = SingleTag.SelectNodes("//MultilingualTextItem");
                foreach (XmlNode ID in IDs)
                {
                    ID.Attributes["ID"].Value = number.ToString() + ID.Attributes["ID"].Value;
                }

                XmlNode InsertTag = FileTagTable.ImportNode(FileTag.SelectSingleNode("//Hmi.Tag.Tag"), true);
                TagTable.AppendChild(InsertTag);
                number++;
            }

            FileTagTable.Save(Receipt.PathObject + FileNameTagTable);
            return FileNameTagTable;
        }

        

        //Import a Single Screen from XML File
        /// <summary>
        /// Importování jednoho screenu z XML souboru do projektu
        /// </summary>
        /// <param name="hmitarget">HMI software, do kterého má být screen importován</param>
        /// <param name="Path">Cesta ke XML souboru se screenem včetně jeho jména</param>
        private static void ImportSingleScreen(HmiTarget hmitarget, string Path)
        {
            //ScreenUserFolder folder = hmitarget.ScreenFolder.Folders.Create("MyFolder");
            ScreenSystemFolder folder = hmitarget.ScreenFolder;
            ScreenComposition screens = folder.Screens;
            screens.Import(new FileInfo(Path),
            ImportOptions.Override);
        }


        //Imports a single HMI tag table from a XML file
        /// <summary>
        /// Importování tag tabulky z XML souboru do projektu
        /// </summary>
        /// <param name="hmitarget">HMI software, do kterého má být tag tabulka importována</param>
        /// <param name="Path">Cesta ke XML souboru se screenem včetně jeho jména</param>
        private static void ImportSingleHMITagTable(HmiTarget hmitarget, string Path)
        {
            TagSystemFolder folder = hmitarget.TagFolder;
            TagTableComposition tables = folder.TagTables;
            FileInfo info = new FileInfo(Path);
            tables.Import(info, ImportOptions.Override);
        }



        /// <summary>
        /// Export všech screenů do XML souborů
        /// </summary>
        /// <param name="hmitarget">HMI software, z kterého mají být screeny exportovány</param>
        private static void ExportScreensOfDevice(HmiTarget hmitarget)
        {
            //ScreenUserFolder folder = hmitarget.ScreenFolder.Folders.Find("Screens");
            ScreenSystemFolder folder = hmitarget.ScreenFolder;
            ScreenComposition screens = folder.Screens;
            foreach (Screen screen in screens)
            {
                FileInfo info = new FileInfo(string.Format(Receipt.PathObject + "{0}.xml", screen.Name));
                screen.Export(info, ExportOptions.WithDefaults);
            }
        }


        /// <summary>
        /// Získání  exportovaných XML souborů se všemi screeny
        /// </summary>
        /// <param name="project">Otevřený projekt TIA Portal</param>
        public void GetScreen(Project project)
        {
            HmiTarget hmiTarget = GetHmiTarget(project);

            ExportScreensOfDevice(hmiTarget);
        }



        /// <summary>
        /// Export hmi tabulky tagů
        /// </summary>
        /// <param name="hmitarget">HMI software, z kterého má být HMI tag tabulka exportována</param>
        /// <param name="Name">Jméno HMI tag tabulky</param>
        private static void ExportTagTableFromHMITarget(HmiTarget hmitarget, string Name)
        {

            string tableName = Name;
            TagSystemFolder folder = hmitarget.TagFolder;
            TagTableComposition tables = folder.TagTables;
            TagTable table = tables.Find(tableName);
            if (table != null)
            {
                FileInfo info = new FileInfo(string.Format(Receipt.PathObject + "{0}.xml",
                table.Name));
                table.Export(info, ExportOptions.WithDefaults);
            }

            /*
            TagSystemFolder folder = hmitarget.TagFolder;
            TagTableComposition tables = folder.TagTables;
            foreach (TagTable table in tables)
            {
                FileInfo info = new FileInfo(string.Format(@"D:\OpennessSamples\TagTables\{0}.xml",
                table.Name));
                table.Export(info, ExportOptions.WithDefaults);
            }
            */
        }


        /// <summary>
        /// Získání  exportovaného XML souboru s HMI Tag tabulkou
        /// </summary>
        /// <param name="project">Otevřený projekt TIA Portal</param>
        /// <param name="Name">Jméno Tag tabulky</param>
        public void GetTagTable(Project project, string Name)
        {
            HmiTarget hmiTarget = GetHmiTarget(project);
            //Name = "HMI_Tags"
            ExportTagTableFromHMITarget(hmiTarget, Name);
        }


        //ExportIndividualTag
        //Exports a selected tag from a tag table
        /// <summary>
        /// Export jediného HMI tagu z HMI tag tabulky
        /// </summary>
        /// <param name="hmitarget">HMI software, z kterého má být HMI tag exportován</param>
        /// <param name="TagName">Jméno tagu, který má být exportován</param>
        /// <param name="TableName">Jméno tag tabulky, ze které má být tag exportován</param>
        private static void ExportSelectedTagFromTagTable(HmiTarget hmitarget, string TagName, string TableName)
        {
            TagSystemFolder tagFolder = hmitarget.TagFolder;
            TagTable mytable = tagFolder.TagTables.Find("HMI_Tags");
            TagComposition containingTags = mytable.Tags;
            Tag myTag = containingTags.Find("Cidlo1");
            if (myTag != null)
            {
                FileInfo info = new FileInfo(string.Format(Receipt.PathObject+ "{0}.xml", myTag.Name));
                myTag.Export(info, ExportOptions.WithDefaults);
            }
        }


        /// <summary>
        /// Získání  exportovaného XML souboru s HMI tagem
        /// </summary>
        /// <param name="project">Otevřený projekt TIA Portal</param>
        /// <param name="TagName">Jméno tagu, který má být exportován</param>
        /// <param name="TableName">Jméno tag tabulky, ze které má být tag exportován</param>
        public void GetTag(Project project, string TagName, string TableName)
        {
            HmiTarget hmiTarget = GetHmiTarget(project);
            //TableName = "HMI_Tags";
            //TagName = "Cidlo1";
            ExportSelectedTagFromTagTable(hmiTarget, TagName, TableName);
        }


        /// <summary>
        /// Export Bloku
        /// </summary>
        /// <param name="project">Otevřený projekt TIA Portal</param>
        /// <param name="Name">Jméno bloku, který má být exportován</param>
        public void ExportBlock(Project project, string Name)
        {
            PlcSoftware plcSoftware = GetPlcSoftware(project);
            //Name = "Main";
            PlcBlock plcBlock = plcSoftware.BlockGroup.Blocks.Find(Name);
            plcBlock.Export(new FileInfo(string.Format(Receipt.PathObject + "{0}.xml", plcBlock.Name)),
ExportOptions.WithDefaults);
        }




    }
}