using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using DevExpress.XtraEditors;
using DevExpress.XtraTreeList;
using DevExpress.XtraTreeList.Native;
using DevExpress.XtraTreeList.Nodes;
using PXP.Crawler.API.Model;
using PXP.Crawler.API.Utils;

namespace PXP.Crawler.API.Designers.Controls
{
    public partial class ProjectDesignerControl : UserControl
    {
        #region Variables

        public static bool ctrlDown = false;
        public static bool altDown = false;
        public static bool shiftDown = false;

        internal Project project = null;
        internal bool hasProject = false;
        private bool ignoreItemsChange = false;

        public static bool EnableDebug = false;

        private bool enableNodeEdit = true;

        #endregion

        #region Constructor

        public ProjectDesignerControl()
        {
            InitializeComponent();

            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.ResizeRedraw, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            SetStyle(ControlStyles.Selectable, true);
            TabStop = true;

            UpdateStyles();

            }

        #endregion

        #region Properties

        private bool enableEdit = true;

        [Category("Behavior")]
        public bool EnableEdit
        {
            get
            {
                return enableEdit;
            }
            set
            {
                enableEdit = value;
                //editBar.Visible = enableEdit;

                
                explorerTreeList.OptionsBehavior.Editable = enableEdit;
            }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Project Project
        {
            get
            {
                return project;
            }
            set
            {
                project = value;
                hasProject = project != null;
                
                explorerTreeList.BeginUnboundLoad();

                explorerTreeList.Nodes.Clear();
                

                if (hasProject)
                {
                    TreeListNode rootNode = explorerTreeList.AppendNode(new object[] { project.rootProjectItem }, null);
                    rootNode.Tag = project.rootProjectItem;
                    project.RootProjectItem.Changed += RootProjectItemOnChanged;
                    project.RootProjectItem.items.Added += items_Added;
                    addBarSubItem.Enabled = true;
                    UpdateNode(rootNode);
                    SetNodeImage(rootNode);
                    rootNode.Expanded = true;
                    rootNode.ExpandAll();

                    if (project.externalSettings != null)
                    {
                        EnableEdit = enableEditBarCheckItem.Checked = bool.Parse(project.externalSettings.GetSett("ProjectDesigner", "EditEnable", "True"));
                        enableProperyGridBarCheckItem.Checked = bool.Parse(project.externalSettings.GetSett("ProjectDesigner", "EnablePropertyGrid", "True"));
                    }
                }

                explorerTreeList.EndUnboundLoad();
            }
        }

        public Control ExplorerControl
        {
            get
            {
                return explorerTreeList;
            }
        }

        public ProjectItem SelectedItem
        {
            get
            {
                return (ProjectItem)explorerTreeList.FocusedNode.Tag;
            }
        }

        #endregion

        #region Methods

        private void SetNodeImage(TreeListNode node)
        {
            ProjectItem pi = (ProjectItem)node.Tag;
            switch (pi.type)
            {
                case ProjectItem.ProjectItemType.Folder:
                    node.ImageIndex = explorerImageCollection.Images.IndexOf(explorerImageCollection.Images["folder"]);
                    node.SelectImageIndex = node.ImageIndex;
                    break;
                case ProjectItem.ProjectItemType.Program:
                    node.ImageIndex = explorerImageCollection.Images.IndexOf(explorerImageCollection.Images["program"]);
                    node.SelectImageIndex = node.ImageIndex;
                    break;
                case ProjectItem.ProjectItemType.Script:
                    node.ImageIndex = explorerImageCollection.Images.IndexOf(explorerImageCollection.Images["script"]);
                    node.SelectImageIndex = node.ImageIndex;
                    break;
                case ProjectItem.ProjectItemType.Project:
                    node.ImageIndex = explorerImageCollection.Images.IndexOf(explorerImageCollection.Images["project"]);
                    node.SelectImageIndex = node.ImageIndex;
                    break;
            }
        }

        private void UpdateNode(TreeListNode parentNode)
        {
            parentNode.Nodes.Clear();
            ProjectItem pi = (ProjectItem) parentNode.Tag;
            foreach (ProjectItem cpi in pi.items)
            {
                TreeListNode cNode = explorerTreeList.AppendNode(new object[] { cpi }, parentNode, cpi);
                cNode.Tag = cpi;
                SetNodeImage(cNode);
                UpdateNode(cNode);
            }
        }

        private TreeListNode FindNode(TreeListNode parentNode, ProjectItem pi)
        {
            if (parentNode.Tag == pi)
                return parentNode;

            foreach (TreeListNode cNode in parentNode.Nodes)
            {
                TreeListNode fNode = FindNode(cNode, pi);
                if (fNode != null)
                    return fNode;
            }

            return null;
        }

        private TreeListNode FindNode(ProjectItem pi)
        {            
            return FindNode(explorerTreeList.Nodes[0], pi);
        }
        #endregion

        #region Internal Events

        private void RootProjectItemOnChanged(object sender, ListEx<ProjectItem>.ListExItemEventArgs<ProjectItem> listExItemEventArgs)
        {
            TreeListNode fNode = FindNode(listExItemEventArgs.Item);
            if (fNode == explorerTreeList.Nodes[0])
            {
                //fNode.Tag = listExItemEventArgs.Item;
                fNode.SetValue(0, listExItemEventArgs.Item.name);
            }            
        }

        void items_Added(object sender, Utils.ListEx<ProjectItem>.ListExItemEventArgs<ProjectItem> e)
        {
            if (ignoreItemsChange)
                return;


            explorerTreeList.BeginUnboundLoad();
            
            TreeListNode parentNode = FindNode(e.Item.parent);

            TreeListNode iNode = explorerTreeList.AppendNode(new object[] { e.Item }, parentNode, e.Item);            
            
            SetNodeImage(iNode);

            explorerTreeList.EndUnboundLoad();

            explorerTreeList.FocusedNode = iNode;
        }

        private void addScriptBarButtonItem_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
        {
            TreeListNode parentNode = explorerTreeList.FocusedNode;
            ProjectItem parentItem = (ProjectItem)parentNode.Tag;

            ProjectItem pi = new ProjectItem();
            pi.Type = ProjectItem.ProjectItemType.Script;
            pi.Name = "New Script";
            pi.projectObjectID = project.GetNewProgramID();
            parentItem.Items.Add(pi);

            OnOpenItem(pi);

            explorerTreeList.FocusedNode = FindNode(pi);
        }

        private void addFolderBarButtonItem_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
        {
            TreeListNode parentNode = explorerTreeList.FocusedNode;
            ProjectItem parentItem = (ProjectItem)parentNode.Tag;

            ProjectItem pi = new ProjectItem();            
            pi.Type = ProjectItem.ProjectItemType.Folder;
            pi.Name = "New Folder";
            pi.projectObjectID = project.GetNewProgramID();
            parentItem.Items.Add(pi);
        }

        private void explorerTreeList_FocusedNodeChanged(object sender, DevExpress.XtraTreeList.FocusedNodeChangedEventArgs e)
        {            
            if (e.Node == null)
            {
                deleteBarButtonItem.Enabled = false;
            }
            else
            {
                ProjectItem pi = (ProjectItem)e.Node.Tag;
                if (pi != null)
                {
                    addBarSubItem.Enabled = pi.type == ProjectItem.ProjectItemType.Folder |
                                            pi.type == ProjectItem.ProjectItemType.Project;
                }
                else
                {
                    addBarSubItem.Enabled = false;
                }
                deleteBarButtonItem.Enabled = pi != project.rootProjectItem;
            }
        }

        private void explorerTreeList_ValidatingEditor(object sender, DevExpress.XtraEditors.Controls.BaseContainerValidateEditorEventArgs e)
        {
            if (e.Value.ToString().Trim().Length == 0)
            {
                e.Valid = false;
            }
            else
            {
                ProjectItem pi = (ProjectItem)explorerTreeList.FocusedNode.Tag;
                pi.Name = e.Value.ToString();
            }
        }

        private void explorerTreeList_ShowingEditor(object sender, CancelEventArgs e)
        {
            if (!enableNodeEdit)
            {
                e.Cancel = true;
            } else if (explorerTreeList.FocusedNode == explorerTreeList.Nodes[0])
            {
                e.Cancel = true;
            }
        }

        private void addProgramBarButtonItem_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
        {
            if (explorerTreeList.FocusedNode.Tag is ProjectItem)
            {                
                ProjectItem fpi = (ProjectItem)explorerTreeList.FocusedNode.Tag;
                if (fpi.type == ProjectItem.ProjectItemType.Folder || fpi.type == ProjectItem.ProjectItemType.Project)
                {                    
                    TreeListNode parentNode = explorerTreeList.FocusedNode;
                    ProjectItem parentItem = (ProjectItem)parentNode.Tag;

                    ProjectItem pi = new ProjectItem();
                    pi.Type = ProjectItem.ProjectItemType.Program;

                    Program program = new Program(project);
                    program.id = project.GetNewProgramID();

                    pi.Name = "Program " + program.id;

                    project.programs.Add(program);
                    pi.projectObjectID = program.id;
                    pi.projectObject = program;

                    parentItem.Items.Add(pi);

                    OnOpenItem(pi);

                    explorerTreeList.FocusedNode = parentNode;
                }                                
            }
        }

        private void explorerTreeList_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Clicks == 2)
            {
                if (e.Button == MouseButtons.Left)
                {
                    if (explorerTreeList.FocusedNode != null)
                    {
                        ProjectItem pi = (ProjectItem) explorerTreeList.FocusedNode.Tag;
                        OnOpenItem(pi);
                    }
                }
            }
        }

        private void deleteBarButtonItem_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
        {
            if (
                XtraMessageBox.Show("Are you sure you want to delete selected item?", "Project Explorer",
                    MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) ==
                DialogResult.Yes)
            {
                ProjectItem pi = (ProjectItem)explorerTreeList.FocusedNode.Tag;
                if (pi.projectObject is Program)
                {
                    project.programs.Remove((Program)pi.projectObject);
                    ((Program)pi.projectObject).Dispose();
                }

                if (pi.projectObject is IDisposable)
                {
                    ((IDisposable)pi.projectObject).Dispose();
                }
                pi.parent.items.Remove(pi);
                explorerTreeList.DeleteNode(explorerTreeList.FocusedNode);

                OnRemoveItem(pi);
            }
        }
        
        private void explorerTreeList_BeforeCollapse(object sender, BeforeCollapseEventArgs e)
        {
            if (e.Node == explorerTreeList.Nodes[0])
            {
                e.CanCollapse = false;
            }
        }

        #endregion

        #region Region

        public event ProjectItemEventHandler OpenItem;

        private void OnOpenItem(ProjectItem projectItem)
        {
            if (OpenItem != null)
                OpenItem(this, new ProjectItemEventArgs(projectItem));
        }

        public event ProjectItemEventHandler RemoveItem;

        private void OnRemoveItem(ProjectItem projectItem)
        {
            if (RemoveItem != null)
                RemoveItem(this, new ProjectItemEventArgs(projectItem));
        }

        #endregion

        #region Drag

        private void explorerTreeList_BeforeDragNode(object sender, BeforeDragNodeEventArgs e)
        {
            if (e.Node == explorerTreeList.Nodes[0])
            {
                e.CanDrag = false;
            }
            else
            {
                e.CanDrag = true;
            }
        }

        private DragDropEffects GetDragDropEffect(TreeList tl, TreeListNode dragNode)
        {
            TreeListNode targetNode;
            Point p = tl.PointToClient(MousePosition);
            targetNode = tl.CalcHitInfo(p).Node;

            if (dragNode != null && targetNode != null && dragNode != targetNode)
            {
                ProjectItem tpi = (ProjectItem) targetNode.Tag;
                if (tpi.type == ProjectItem.ProjectItemType.Project || tpi.type == ProjectItem.ProjectItemType.Folder)
                {
                    return DragDropEffects.Move;
                }
                else
                {
                    return DragDropEffects.None;
                }
            }
            else
                return DragDropEffects.None;
        }

        private void explorerTreeList_DragOver(object sender, DragEventArgs e)
        {
            TreeListNode dragNode = e.Data.GetData(typeof (TreeListNode)) as TreeListNode;
            e.Effect = GetDragDropEffect(sender as TreeList, dragNode);
        }

        private void explorerTreeList_DragDrop(object sender, DragEventArgs e)
        {


            TreeListNode dragNode, targetNode;
            TreeList tl = sender as TreeList;
            Point p = tl.PointToClient(new Point(e.X, e.Y));

            dragNode = e.Data.GetData(typeof (TreeListNode)) as TreeListNode;
            targetNode = tl.CalcHitInfo(p).Node;

            ProjectItem dragItem = (ProjectItem)dragNode.Tag;
            ProjectItem targetItem = (ProjectItem)targetNode.Tag;

            ignoreItemsChange = true;

            dragItem.parent.items.Remove(dragItem);
            targetItem.items.Add(dragItem);

            explorerTreeList.BeginUpdate();

            tl.SetNodeIndex(dragNode, tl.GetNodeIndex(targetNode));
            dragNode.ParentNode.Nodes.Remove(dragNode);

            TreeListNode ndNode = explorerTreeList.AppendNode(new object[] {dragNode.Tag}, targetNode, dragNode.Tag);
            ndNode.Tag = dragNode.Tag;
            SetNodeImage(ndNode);
            UpdateNode(ndNode);

            explorerTreeList.EndUpdate();
            explorerTreeList.Update();
            e.Effect = DragDropEffects.None;

            ignoreItemsChange = false;
        }

        private void explorerTreeList_CalcNodeDragImageIndex(object sender,
            CalcNodeDragImageIndexEventArgs e)
        {
            TreeList tl = sender as TreeList;
            if (GetDragDropEffect(tl, tl.FocusedNode) == DragDropEffects.None)
                e.ImageIndex = -1;
            else
                e.ImageIndex = 2;
        }

        #endregion

        private void ProjectDesignerControl_Load(object sender, EventArgs e)
        {
            //addProgramBarButtonItem.PerformClick();
            if (DesignMode)
                return;
            
            project.Compiler.CompileResult += new ProjectCompilerResultEventHandler(ProjectCompileResult);
        }

        #region Events

        public event ProjectCompilerResultEventHandler CompileResult;

        private void OnCompileResult(ProjectCompilerResultEventArgs e)
        {            
            if (CompileResult != null)
                CompileResult(this, e);
        }

        #endregion
        private void ProjectCompileResult(object sender, ProjectCompilerResultEventArgs e)
        {
            OnCompileResult(e);
        }

        private void barButtonItem1_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
        {

        }

        #region Events

        public event EventHandler SelectionChanged;

        private void OnSelChanged()
        {
            if (SelectionChanged != null)
                SelectionChanged(this, EventArgs.Empty);
        }

        private void explorerTreeList_AfterFocusNode(object sender, NodeEventArgs e)
        {
            OnSelChanged();
            if (enableProperyGridBarCheckItem.Down)
            {
                OnDoSelectItemInPropertyGrid();
            }
        }

        #endregion

        public event EventHandler DoSelectItemInPropertyGrid;

        private void OnDoSelectItemInPropertyGrid()
        {
            if (DoSelectItemInPropertyGrid != null)
                DoSelectItemInPropertyGrid(this, EventArgs.Empty);
        }



        private void enableProperyGridBarCheckItem_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
        {
            if (project.externalSettings != null)
            {
                project.externalSettings.SetSett("ProjectDesigner", "EditEnable", enableEditBarCheckItem.Checked.ToString());
                project.externalSettings.SetSett("ProjectDesigner", "EnablePropertyGrid", enableProperyGridBarCheckItem.Checked.ToString());
            }

        }

        private void enableEditBarCheckItem_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
        {
            EnableEdit = enableEditBarCheckItem.Checked;

            if (project.externalSettings != null)
            {
                project.externalSettings.SetSett("ProjectDesigner", "EditEnable", enableEditBarCheckItem.Checked.ToString());
                project.externalSettings.SetSett("ProjectDesigner", "EnablePropertyGrid", enableProperyGridBarCheckItem.Checked.ToString());
            }
        }

        private void explorerTreeList_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            TreeListHitInfo hi = explorerTreeList.CalcHitInfo(e.Location);
            if (hi.HitInfoType == HitInfoType.Cell)
            {
                if (explorerTreeList.FocusedNode != null)
                {
                    ProjectItem pi = (ProjectItem)explorerTreeList.FocusedNode.Tag;
                    if (pi.type == ProjectItem.ProjectItemType.Folder)
                    {
                        explorerTreeList.OptionsBehavior.Editable = true;
                        explorerTreeList.ShowEditor();
                    }

                }
            }
            

        }

        private void explorerTreeList_HiddenEditor(object sender, EventArgs e)
        {
            if (!enableEditBarCheckItem.Checked)
            {
                explorerTreeList.OptionsBehavior.Editable = false;
            }
        }

        private void enableProperyGridBarCheckItem_DownChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
        {
            if (project.externalSettings != null)
            {
                project.externalSettings.SetSett("ProjectDesigner", "EditEnable", enableEditBarCheckItem.Checked.ToString());
                project.externalSettings.SetSett("ProjectDesigner", "EnablePropertyGrid", enableProperyGridBarCheckItem.Checked.ToString());
            }
        }

        private void barButtonItem1_ItemClick_1(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
        {
            ParentForm.Cursor = Cursors.WaitCursor;
            //saveBarButtonItem.PerformClick();
            project.Compiler.BuildProject();
            ParentForm.Cursor = Cursors.Default;
        }





    }
}
