using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Windows.Forms;
using SolidEdgePart;

namespace ReGen
{
    public partial class ReGenMainForm : Form
    {
        [DllImport("User32.dll")]
        private static extern IntPtr LoadCursorFromFile(String str);

        private SolidEdgeFramework.Application m_Application = null;
        private PartDocument m_Document = null;
        private GenerativeStudies m_Studies = null;
        private Boolean m_KillSolidEdgeProcesses = false;
        private Boolean m_ResultsDlgSizeAndPositionInitialized = false;
        private Boolean m_UpdateInProgress = false;
        private Point m_ResultsLocation = new Point();
        private Size m_ResultsSize = new Size();
        private List<String> m_ProcessedDocuments = new List<String>();
        private DocumentResults m_DocumentResults = new DocumentResults();
        private Cursor m_WaitCursor = null;

        private static Cursor CreateAnimatedCursor(string CursorFile)
        {
            // how to handle cursor.
            IntPtr CursorHandle = LoadCursorFromFile(CursorFile);

            // Check  wheather it is succeeded or not.
            if (!IntPtr.Zero.Equals(CursorHandle))
            {
                return new Cursor(CursorHandle);
            }
            else
            {
                return null;
            }
        }

        public ReGenMainForm()
        {
            InitializeComponent();
        }

        private void Cleanup()
        {
            CleanupDocumentAndStudies();
            CleanupApplication();
        }

        private void CleanupApplication()
        {
            m_Application = null;

            if (m_KillSolidEdgeProcesses)
            {
                CloseZombieMeshers();
                CloseZombieOptimizers();
                CloseSolidEdgeProcesses();
            }
        }

        private void CleanupDocumentAndStudies()
        {
            m_Studies = null;

            if (null != m_Document)
            {
                try
                {
                    m_Document.Close(false);
                }
                catch
                {
                }
            }

            m_Document = null;
        }

        private void CloseZombieOptimizers()
        {
            try
            {
                Process[] Processes = Process.GetProcessesByName("Optimizer");

                foreach (Process OptimizerProcess in Processes)
                {
                    try
                    {
                        OptimizerProcess.Kill();
                        OptimizerProcess.WaitForExit();
                        OptimizerProcess.Dispose();
                    }
                    catch
                    {
                    }
                }
            }
            catch
            {
            }
        }

        private void CloseZombieMeshers()
        {
            try
            {
                Process[] Processes = Process.GetProcessesByName("mesher");

                foreach (Process MesherProcess in Processes)
                {
                    try
                    {
                        MesherProcess.Kill();
                        MesherProcess.WaitForExit();
                        MesherProcess.Dispose();
                    }
                    catch
                    {
                    }
                }
            }
            catch
            {
            }
        }

        private void CloseSolidEdgeProcesses()
        {
            try
            {
                Process[] Processes = Process.GetProcessesByName("Edge");

                foreach (Process SolidEdgeProcess in Processes)
                {
                    try
                    {
                        SolidEdgeProcess.Kill();
                        SolidEdgeProcess.WaitForExit();
                        SolidEdgeProcess.Dispose();
                    }
                    catch
                    {
                    }
                }
            }
            catch
            {
            }
        }

        private void InitializeCustomCursor()
        {
            try
            {
                m_WaitCursor = CreateAnimatedCursor(Path.Combine(Application.StartupPath, "Updating.ani"));
            }
            catch
            {
                m_WaitCursor = null;
            }
        }

private void SetApplicationPointer()
        {
            try
            {
                if (null == m_Application)
                {
                    try
                    {
                        m_Application = (SolidEdgeFramework.Application)Marshal.GetActiveObject("SolidEdge.Application");
                    }
                    catch
                    {
                    }

                    if (null == m_Application)
                    {
                        Type ComType = Type.GetTypeFromProgID("SolidEdge.Application");

                        if (null != ComType)
                        {
                            m_Application = (SolidEdgeFramework.Application)Activator.CreateInstance(ComType);

                            m_KillSolidEdgeProcesses = true;
                        }
                    }
                }
            }
            catch
            {
            }
        }

        private void SetDocumentAndStudiesPointersForDocument(String DocumentPath, ref bool ErrorOccurred)
        {
            ErrorOccurred = false;

            if (!String.IsNullOrWhiteSpace(DocumentPath))
            {
                try
                {
                    if (null == m_Document)
                    {
                        if (null != m_Application)
                        {
                            m_Document = (PartDocument)m_Application.Documents.Open(DocumentPath);
                        }
                    }

                    if (null == m_Studies)
                    {
                        if (null != m_Document)
                        {
                            m_Studies = m_Document.GenerativeStudies;
                        }
                    }
                }
                catch
                {
                    ErrorOccurred = true;

                    try
                    {
                        Cleanup();
                        SetApplicationPointer();
                    }
                    catch
                    {

                    }
                }

                if ((null == m_Document) || (null == m_Studies))
                {
                    AddAccessingDocumentAndStudiesError(DocumentPath);
                }
            }
        }

        private void AddAccessingDocumentAndStudiesError(String DocumentPath)
        {
            if (!String.IsNullOrWhiteSpace(DocumentPath))
            {
                try
                {
                    List<String> ResultsForStudy = new List<String>();

                    if ((null != m_ProcessedDocuments) && !m_ProcessedDocuments.Contains(DocumentPath))
                    {
                        m_ProcessedDocuments.Add(DocumentPath);
                    }

                    if ((null != ResultsForStudy) && (null != m_DocumentResults))
                    {
                        ResultsForStudy.Add("An unknown error occurred while trying to access the part and its Generative Studies.");

                        m_DocumentResults.SetResultsForStudy(DocumentPath, "Information", ResultsForStudy);
                    }
                }
                catch
                {
                }
            }
        }

        private void AddProcessedDocumentsToResultsDialog(ref DocumentResultsForm ResultsDlg)
        {
            if (null != ResultsDlg)
            {
                if ((null != m_ProcessedDocuments) && (0 != m_ProcessedDocuments.Count))
                {
                    try
                    {
                        ResultsDlg.SetProcessedDocuments(m_ProcessedDocuments);
                    }
                    catch
                    {
                    }
                }
            }
        }

        private void InitializeResultsDialogSizeAndPosition()
        {
            m_ResultsLocation = this.Location;
            m_ResultsSize = this.Size;

            m_ResultsDlgSizeAndPositionInitialized = true;
        }

        private void SetResultsDialogSizeAndPosition(ref DocumentResultsForm ResultsDlg)
        {
            if (null != ResultsDlg)
            {
                try
                {
                    if (String.IsNullOrWhiteSpace(Properties.Settings.Default.DocumentResultsInitialLocation))
                    {
                        ResultsDlg.Location = m_ResultsLocation;
                        ResultsDlg.Size = m_ResultsSize;
                    }
                }
                catch
                {
                }
            }
        }

        private void SaveResultsDialogSizeAndPosition(DocumentResultsForm ResultsDlg)
        {
            if (null != ResultsDlg)
            {
                try
                {
                    m_ResultsLocation = ResultsDlg.Location;
                    m_ResultsSize = ResultsDlg.Size;
                }
                catch
                {
                }
            }
        }

        private void ShowResultsDialog()
        {
            try
            {
                DocumentResultsForm ResultsDlg = new DocumentResultsForm();

                if (null != ResultsDlg)
                {
                    if (null != m_DocumentResults)
                    {
                        ResultsDlg.SetResults(m_DocumentResults);
                    }

                    AddProcessedDocumentsToResultsDialog(ref ResultsDlg);

                    if (!m_ResultsDlgSizeAndPositionInitialized)
                    {
                        InitializeResultsDialogSizeAndPosition();
                    }

                    SetResultsDialogSizeAndPosition(ref ResultsDlg);

                    ResultsDlg.ShowDialog();

                    SaveResultsDialogSizeAndPosition(ResultsDlg);
                }
            }
            catch
            {
            }
        }

        private void ProcessDocumentResults()
        {
            try
            {
                ShowResultsDialog();

                SetDeleteButtonState();
                SetUpdateButtonState();
            }
            catch
            {
            }

            if (null != m_ProcessedDocuments)
            {
                m_ProcessedDocuments.Clear();
            }
        }

        private void UpdateStudyResults()
        {
            StatusTextLabel.Text = "Initializing";

            SetApplicationPointer();
            CleanupDocumentAndStudies();

            if (null != m_ProcessedDocuments)
            {
                m_ProcessedDocuments.Clear();
            }

            try
            {
                while (0 != DocumentListBox.Items.Count)
                {
                    String DocumentPath = DocumentListBox.Items[0].ToString();

                    if (!String.IsNullOrWhiteSpace(DocumentPath))
                    {
                        bool ErrorOccurred = false;

                        SetDocumentAndStudiesPointersForDocument(DocumentPath, ref ErrorOccurred);

                        if (false == ErrorOccurred)
                        {
                            ProcessStudies(DocumentPath);

                            if ((null != m_ProcessedDocuments) && !m_ProcessedDocuments.Contains(DocumentPath))
                            {
                                m_ProcessedDocuments.Add(DocumentPath);
                            }
                        }

                        CleanupDocumentAndStudies();

                        DocumentListBox.Items.Remove(DocumentPath);
                    }
                }
            }
            catch
            {
            }

            SetUpdateButtonState();

            Cleanup();

            ProcessDocumentResults();

            StatusTextLabel.Text = "Waiting";
        }

        private String GenerateSolveStatusString(String StudyName, String DocumentPath)
        {
            if (!String.IsNullOrWhiteSpace(StudyName))
            {
                String StatusString = "Generating: ";
                bool DocumentNameAdded = false;

                try
                {
                    if (null != m_Document)
                    {
                        String DocumentName = m_Document.Name;

                        if (!String.IsNullOrWhiteSpace(DocumentName))
                        {
                            StatusString += DocumentName + "  ( ";

                            DocumentNameAdded = true;
                        }
                    }
                }
                catch
                {
                }

                StatusString += StudyName;

                if (DocumentNameAdded)
                {
                    StatusString += " )";
                }

                return StatusString;
            }

            return "Generating";
        }

        private void SaveDocument()
        {
            if (null != m_Document)
            {
                try
                {
                    m_Document.Save();
                }
                catch
                {
                }
            }
        }

        private void AddNoStudiesInDocumentToResults(String DocumentPath)
        {
            if (!String.IsNullOrWhiteSpace(DocumentPath))
            {
                try
                {
                    List<String> ResultsForStudy = new List<String>();

                    if ((null != ResultsForStudy) && (null != m_DocumentResults))
                    {
                        ResultsForStudy.Add("There are no Generative Studies in the part.");

                        m_DocumentResults.SetResultsForStudy(DocumentPath, "Information", ResultsForStudy);
                    }
                }
                catch
                {
                }
            }
        }

        private String StudyNotReadyReasonToString(GenerativeStudyNotReadyForSolveReasons Reason)
        {
            String ReasonString = "";

            switch (Reason)
            {
                case GenerativeStudyNotReadyForSolveReasons.seGenerativeDesignStudyNotReadyForSolveAllLoadCasesSuppressed:
                    ReasonString = "All load cases are suppressed.";
                break;

                case GenerativeStudyNotReadyForSolveReasons.seGenerativeDesignStudyNotReadyForSolveDesignSpaceUndefined:
                    ReasonString = "The design space has not been defined.";
                break;

                case GenerativeStudyNotReadyForSolveReasons.seGenerativeDesignStudyNotReadyForSolveMaterialNotDefined:
                    ReasonString = "The design space does not have a material applied to it.";
                break;

                case GenerativeStudyNotReadyForSolveReasons.seGenerativeDesignStudyNotReadyForSolveDoesNotHaveAtLeastOneValidLoadCase:
                    ReasonString = "The study does not have at least one valid load case.";
                break;

                case GenerativeStudyNotReadyForSolveReasons.seGenerativeDesignStudyNotReadyForSolveEachLoadCaseMustHaveLoadAndConstraint:
                    ReasonString = "Each load case must have at least one unsuppressed constraint and either one unsuppressed load or have Gravity defined.";
                break;
            }

            return ReasonString;
        }

        private void AddStudyNotReadyForSolve(GenerativeStudy Study, String DocumentPath)
        {
            if ((null != Study) && !String.IsNullOrWhiteSpace(DocumentPath))
            {
                try
                {
                    List<String> ResultsForStudy = new List<String>();

                    if ((null != ResultsForStudy) && (null != m_DocumentResults))
                    {
                        Array NotReadyToSolveReasons = new GenerativeStudyNotReadyForSolveReasons[5];

                        if (null != NotReadyToSolveReasons)
                        {
                            bool ReadyToSolve = Study.IsReadyToSolveDetails(out NotReadyToSolveReasons);

                            if (0 != NotReadyToSolveReasons.Length)
                            {
                                foreach (GenerativeStudyNotReadyForSolveReasons Reason in NotReadyToSolveReasons)
                                {
                                    String ReasonToAdd = StudyNotReadyReasonToString(Reason);

                                    if (!String.IsNullOrWhiteSpace(ReasonToAdd))
                                    {
                                        ResultsForStudy.Add(ReasonToAdd);
                                    }
                                }
                            }
                        }

                        if ((null == NotReadyToSolveReasons) || (0 == NotReadyToSolveReasons.Length))
                        {
                            ResultsForStudy.Add("Not ready to solve.");
                        }

                        m_DocumentResults.SetResultsForStudy(DocumentPath, Study.Name, ResultsForStudy);
                    }
                }
                catch
                {
                }
            }
        }

        private void SolveStudy(GenerativeStudy Study, String DocumentPath)
        {
            if ((null != Study) && !String.IsNullOrWhiteSpace(DocumentPath))
            {
                List<String> ResultsForStudy = new List<String>();

                try
                {
                    Array Errors = new String[1];

                    Study.Solve(out Errors);

                    if ((null != ResultsForStudy) && (null != m_DocumentResults))
                    {
                        if ((null == Errors) || (0 == Errors.Length))
                        {
                            ResultsForStudy.Add("No errors reported.");
                        }
                        else
                        {
                            foreach (String Error in Errors)
                            {
                                ResultsForStudy.Add(Error);
                            }
                        }

                        m_DocumentResults.SetResultsForStudy(DocumentPath, Study.Name, ResultsForStudy);
                    }

                    SaveDocument();
                }
                catch
                {
                    if ((null != ResultsForStudy) && (null != m_DocumentResults))
                    {
                        ResultsForStudy.Add("An unknown exception occurred while trying to solve.");

                        m_DocumentResults.SetResultsForStudy(DocumentPath, Study.Name, ResultsForStudy);
                    }
                }
            }
        }

        private void ProcessStudy(GenerativeStudy Study, String DocumentPath)
        {
            if ((null != Study) && !String.IsNullOrWhiteSpace(DocumentPath))
            {
                try
                {
                    if (Study.IsReadyForSolve)
                    {
                        StatusTextLabel.Text = GenerateSolveStatusString(Study.Name, DocumentPath);

                        try
                        {
                            SolveStudy(Study, DocumentPath);
                        }
                        catch
                        {
                        }
                    }
                    else
                    {
                        AddStudyNotReadyForSolve(Study, DocumentPath);
                    }
                }
                catch
                {
                }
            }
        }

        private void ProcessStudies(String DocumentPath)
        {
            try
            {
                if ((null != m_Studies) && (!String.IsNullOrWhiteSpace(DocumentPath)))
                {
                    if (0 == m_Studies.Count)
                    {
                        AddNoStudiesInDocumentToResults(DocumentPath);
                    }
                    else
                    {
                        foreach (GenerativeStudy Study in m_Studies)
                        {
                            try
                            {
                                ProcessStudy(Study, DocumentPath);
                            }
                            catch
                            {
                            }
                        }
                    }

                    StatusTextLabel.Text = "Finished";
                }
            }
            catch
            {
            }
        }

        private void CloseButton_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void InitializeOpenFileDialogSettings(ref OpenFileDialog Dialog)
        {
            if (null != Dialog)
            {
                Dialog.Filter = "Solid Edge Part Files|*.par";
                Dialog.Title = "Select Part(s)";
                Dialog.Multiselect = true;
            }
        }

        void ProcessOpenFileDialogResults(OpenFileDialog Dialog)
        {
            if (null != Dialog)
            {
                foreach (String DocumentPath in Dialog.FileNames)
                {
                    try
                    {
                        if (!DocumentListBox.Items.Contains(DocumentPath))
                        {
                            DocumentListBox.Items.Add(DocumentPath);
                        }
                    }
                    catch
                    {
                    }
                }
            }
        }

        private void SelectPartButton_Click(object sender, EventArgs e)
        {
            try
            {
                OpenFileDialog SelectPartDialog = new OpenFileDialog();

                if (null != SelectPartDialog)
                {
                    InitializeOpenFileDialogSettings(ref SelectPartDialog);

                    if (DialogResult.OK == SelectPartDialog.ShowDialog())
                    {
                        ProcessOpenFileDialogResults(SelectPartDialog);
                    }

                    SetUpdateButtonState();
                }
            }
            catch
            {
            }
        }

        private void UpdateButton_Click(object sender, EventArgs e)
        {
            m_UpdateInProgress = true;

            SetWaitCursor();

            UpdateStudyResults();

            Cleanup();

            m_UpdateInProgress = false;

            SetDefaultCursor();
        }

        private void SetWaitCursor()
        {
            if (null != m_WaitCursor)
            {
                Cursor.Current = m_WaitCursor;
            }
            else
            {
                Cursor.Current = Cursors.WaitCursor;
            }
        }

        private void SetDefaultCursor()
        {
            Cursor.Current = Cursors.Default;
        }

        private void ReGenForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            SaveSizeAndLocation();

            Cleanup();
        }

        private void SaveSizeAndLocation()
        {
            if (0 == (ModifierKeys & Keys.Shift))
            {
                try
                {
                    Point SaveLocation = this.Location;
                    Size SaveSize = this.Size;

                    if (FormWindowState.Normal != this.WindowState)
                    {
                        SaveLocation = RestoreBounds.Location;
                        SaveSize = RestoreBounds.Size;
                    }

                    String initLocation = String.Join(",", SaveLocation.X, SaveLocation.Y, SaveSize.Width, SaveSize.Height);

                    Properties.Settings.Default.InitialLocation = initLocation;
                    Properties.Settings.Default.Save();
                }
                catch
                {

                }
            }
        }

        private void InitializeSizeAndLocation()
        {
            if (0 == (ModifierKeys & Keys.Shift))
            {
                try
                {
                    String initLocation = Properties.Settings.Default.InitialLocation;
                    Point InitialPoint = this.Location;
                    Size InitialSize = this.Size;

                    if (!String.IsNullOrWhiteSpace(initLocation))
                    {
                        String[] parts = initLocation.Split(',');

                        if (2 <= parts.Length)
                        {
                            InitialPoint = new Point(int.Parse(parts[0]), int.Parse(parts[1]));
                        }

                        if (4 <= parts.Length)
                        {
                            InitialSize = new Size(int.Parse(parts[2]), int.Parse(parts[3]));
                        }
                    }

                    this.Size = InitialSize;
                    this.Location = InitialPoint;
                }
                catch
                {
                }
            }
        }

        private void ReGenForm_Load(object sender, EventArgs e)
        {
            StatusTextLabel.Text = "Waiting";

            SetUpdateButtonState();
            SetDeleteButtonState();

            InitializeCustomCursor();

            InitializeSizeAndLocation();
        }

        private void DeleteSelectedDocumentItems()
        {
            if (0 < DocumentListBox.SelectedItems.Count)
            {
                for (int i = DocumentListBox.SelectedIndices.Count; i > 0; --i)
                {
                    DocumentListBox.Items.RemoveAt(DocumentListBox.SelectedIndices[i - 1]);
                }

                DocumentListBox.SelectedItems.Clear();

                SetUpdateButtonState();
            }
        }

        private void SelectAllDocumentItems()
        {
            for (int i = 0; i < DocumentListBox.Items.Count; ++i)
            {
                DocumentListBox.SetSelected(i, true);
            }
        }

        private void ClearDocumentSelection()
        {
            DocumentListBox.SelectedIndex = -1;
        }

        private void SetUpdateButtonState()
        {
            if (0 == DocumentListBox.Items.Count)
            {
                UpdateButton.Enabled = false;
            }
            else
            {
                UpdateButton.Enabled = true;
            }
        }

        private void SetDeleteButtonState()
        {
            if (0 == DocumentListBox.SelectedItems.Count)
            {
                DeleteButton.Enabled = false;
            }
            else
            {
                DeleteButton.Enabled = true;
            }
        }

        private void DocumentListBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (Keys.Delete == e.KeyCode)
            {
                DeleteSelectedDocumentItems();
            }
            else if (e.Control && (Keys.A == e.KeyCode))
            {
                SelectAllDocumentItems();
            }
            else if (e.Control && (Keys.D == e.KeyCode))
            {
                ClearDocumentSelection();
            }
        }

        private void DeleteButton_Click(object sender, EventArgs e)
        {
            DeleteSelectedDocumentItems();
        }

        private void DocumentListBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            SetDeleteButtonState();
        }

        private void DocumentListBox_MouseClick(object sender, MouseEventArgs e)
        {
            try
            {
                if (ListBox.NoMatches == DocumentListBox.IndexFromPoint(e.Location))
                {
                    ClearDocumentSelection();
                }
            }
            catch
            {
            }
        }

        private void ReGenForm_Activated(object sender, EventArgs e)
        {
            if (m_UpdateInProgress && (Cursor.Current != m_WaitCursor))
            {
                SetWaitCursor();
            }
        }
    }
}
