﻿using System;
using System.Windows;
using System.Windows.Input;
using System.ComponentModel;
using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Prism.PubSubEvents;
using Microsoft.Practices.Prism.Interactivity.InteractionRequest;
using Microsoft.Practices.Prism.Regions;
using Microsoft.Practices.ServiceLocation;
using System.IO;
using SoftMod.Framework.Solution;
using SoftMod.Framework.SQL;
using SoftMod.Infrastructure;
using SoftMod.Infrastructure.InteractionRequests;
using SoftMod.Infrastructure.EventAggregation;
using SoftMod.DataViewer.Views;

namespace SoftMod.DataViewer.ViewModels
{
    public class DataViewerViewModel : Notification, IPopupWindowActionAware, IRegionManagerAware, INotifyPropertyChanged 
    {
        // IPopupWindowActionAware implementation
        public Window HostWindow { get; set; }
        // Host notification
        public Notification HostNotification { get; set; }
        // IRegionManagerAware implementation
        public IRegionManager RegionManager { get; set; }
        // Declare event
        public event PropertyChangedEventHandler PropertyChanged;
        // Declare raise sub tree command
        public ICommand RaiseSubTreeViewer { get; private set; }
        // Declare new data command
        public ICommand RaiseNewDataCommand { get; private set; }
        // Declare open command
        public ICommand RaiseOpenCommand { get; private set; }
        // Declare save command
        public ICommand RaiseSaveCommand { get; private set; }
        // Declare add leaf command
        public ICommand RaiseAddLeafCommand { get; private set; }
        // Declare add branch command
        public ICommand RaiseAddBranchCommand { get; private set; }
        // Declare raise add global marker command
        public ICommand RaiseAddGlobalMarker { get; private set; }
        // Declare raise add node marker command
        public ICommand RaiseAddNodeMarker { get; private set; }
        // Declare raise add global marker command
        public ICommand RaiseUpdateGlobalMarker { get; private set; }
        // Declare variable to hold selected data object root
        private Root _selectedRootObject { get; set; }
        // Declare variable to hold subtree viewer view model
        private SubTreeViewerViewModel subtreeViewerViewModel { get; set; }
        // Declare sub tree view
        public SubTreeViewerView subTreeViewerView { get; private set; }
        // Declare variable to hold selected ndoe view
        private NodeView _selectedNodeView { get; set; }
        // Declare data type object
        public DataType _dataType { get; private set; }
        // Declare data type object
        public NodeMarkerType _nodeMarkerType { get; private set; }
        // Declare subtree viewer flyout bool
        public bool _subTreeFlyout { get; private set; }
        // Declare event aggregator
        public IEventAggregator aggregator { get; set; }
        /// <summary>
        /// Constructor
        /// </summary>
        public DataViewerViewModel()
        {
            // Set delegates
            this.RaiseSubTreeViewer = new DelegateCommand(this.OnRaiseSubTreeViewer);
            this.RaiseNewDataCommand = new DelegateCommand(this.OnRaiseNewDataCommand);
            this.RaiseOpenCommand = new DelegateCommand(this.OnRaiseOpenCommand);
            this.RaiseSaveCommand = new DelegateCommand(this.OnRaiseSaveCommand);
            this.RaiseAddLeafCommand = new DelegateCommand(this.OnRaiseAddLeafCommand);
            this.RaiseAddBranchCommand = new DelegateCommand(this.OnRaiseAddBranchCommand);
            this.RaiseAddGlobalMarker = new DelegateCommand(this.OnRaiseAddGlobalMarker);
            this.RaiseAddNodeMarker = new DelegateCommand(this.OnRaiseAddNodeMarker);
            // Set event aggregator
            this.aggregator = ServiceLocator.Current.GetInstance<IEventAggregator>();
            // Set data type object
            dataType = new DataType();
            // Set default root
            Root root = new Root();
            // Set selected root
            selectedRootObject = root;
            // Set subtree view model
            subtreeViewerViewModel = new SubTreeViewerViewModel(selectedRootObject) { Content = "Sub Tree Viewer", Title = "Sub Tree Viewer" };
            // Create subtree viewer view
            subTreeViewerView = new SubTreeViewerView();
            // Set view model of subtree viewer
            subTreeViewerView.DataContext = subtreeViewerViewModel;
            // Set flyouts
            subTreeViewerFlyoutIsOpen = false;
        }
        // Set constructor for selected pathway node
        public Root selectedRootObject
        {
            get { return _selectedRootObject; }
            set
            {
                _selectedRootObject = value;
                if (null != PropertyChanged)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("selectedRootObject"));
                }
            }
        }
        // Set constructor for selected node view
        public NodeView selectedNodeView
        {
            get { return _selectedNodeView; }
            set
            {
                _selectedNodeView = value;
                if (null != PropertyChanged)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("selectedNodeView"));
                }
            }
        }
        // Set constructor for data type object
        public DataType dataType
        {
            get { return _dataType; }
            set
            {
                _dataType = value;
                if (null != PropertyChanged)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("dataType"));
                }
            }
        }
        // Set constructor for node marker type object
        public NodeMarkerType nodeMarkerType
        {
            get { return _nodeMarkerType; }
            set
            {
                _nodeMarkerType = value;
                if (null != PropertyChanged)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("nodeMarkerType"));
                }
            }
        }
        // Set constructor for subtree viewer flyout bool
        public bool subTreeViewerFlyoutIsOpen
        {
            get { return _subTreeFlyout; }
            set
            {
                _subTreeFlyout = value;
                if (null != PropertyChanged)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("subTreeViewerFlyoutIsOpen"));
                }
            }
        }
        /// <summary>
        /// Delete node view
        /// </summary>
        public void DeleteNodeView()
        {
            // Check that there is a node view selected
            if (this.selectedNodeView != null)
            {
                // Check root node is not selected
                if (!((selectedNodeView.DataContext as NodeViewModel).node is Root))
                {
                    // Remove selected node from root selected root object
                    if ((selectedNodeView.DataContext as NodeViewModel).node.getParent() is Root)
                    {
                        // Remove child
                        ((selectedNodeView.DataContext as NodeViewModel).node.getParent() as Root).RemoveChild((selectedNodeView.DataContext as NodeViewModel).node, true);
                    }
                    else if ((selectedNodeView.DataContext as NodeViewModel).node.getParent() is Branch)
                    {
                        // Remove child
                        ((selectedNodeView.DataContext as NodeViewModel).node.getParent() as Branch).RemoveChild((selectedNodeView.DataContext as NodeViewModel).node, true);
                    }
                }
            }
        }
        /// <summary>
        /// Raise sub tree viewer method
        /// </summary>
        private void OnRaiseSubTreeViewer()
        {
            // Set sub tree viewer flyout bool to true
            subTreeViewerFlyoutIsOpen = true;
        }
        /// <summary>
        /// Raise open menu command method
        /// </summary>
        private void OnRaiseNewDataCommand()
        {
            // Remove current selected root object and replace with a default new one
            selectedRootObject = new Root();
        }
        /// <summary>
        /// Raise open menu command method
        /// </summary>
        private void OnRaiseOpenCommand()
        {
            // Open data viewer open menu
            // Configure open data file dialog box
            Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
            dlg.FileName = "Root Data File"; // Default file name
            dlg.DefaultExt = ".s3db"; // Default file extension
            dlg.Filter = "Data Files (.s3db)|*.s3db"; // Filter files by extension
            // Show open file dialog box
            Nullable<bool> result = dlg.ShowDialog();
            // Process open file dialog box results
            if (result == true)
            {
                // Pass file name
                string filename = dlg.FileName;
                // Save file
                SQLiteRootObject sqliteRoot = new SQLiteRootObject();
                // Call save root object
                selectedRootObject = sqliteRoot.LoadRootObject(filename);
            }
        }
        /// <summary>
        /// Raise open menu command method
        /// </summary>
        private void OnRaiseSaveCommand()
        {
            // Save current selected root node as a binary object in sql
            // Configure open data file dialog box
            Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
            dlg.FileName = "Root Data File"; // Default file name
            dlg.DefaultExt = ".s3db"; // Default file extension
            dlg.Filter = "Data Files (.s3db)|*.s3db"; // Filter files by extension
            dlg.InitialDirectory = Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.FullName +  @"\DataTemplates"; // Set directoy
            // Show open file dialog box
            Nullable<bool> result = dlg.ShowDialog();
            // Process open file dialog box results
            if (result == true)
            {
                // Pass file name
                string filename = dlg.FileName;
                // Save file
                SQLiteRootObject sqliteRoot = new SQLiteRootObject();
                // Call save root object
                sqliteRoot.SaveRootObject(filename, selectedRootObject);
            }
        }
        /// <summary>
        /// Raise add leaf node command method
        /// </summary>
        private void OnRaiseAddLeafCommand()
        {
            // Declare new leaf node variable
            Leaf<VarType<double>> leaf = new Leaf<VarType<double>>();
            // Set var type
            VarType<double> varType = new VarType<double>(0, "Real Valued");
            // Pass data
            leaf.setData(varType);
            // Add leaf node
            // Declare variable to hold root
            Root root = this.selectedRootObject;
            // Get selected node
            if ((selectedNodeView.DataContext as NodeViewModel).node == root)
            {
                // Add leaf node to selected node view
                (root as Root).AddChild(leaf);
            }
            else
            {
                // Add leaf node to selected node view
                ((selectedNodeView.DataContext as NodeViewModel).node as Branch).AddChild(leaf);
            }
            // Call an update of root by setting to null
            this.selectedRootObject = null;
            // Re update with root
            this.selectedRootObject = root;
        }
        /// <summary>
        /// Raise add branch node command method
        /// </summary>
        private void OnRaiseAddBranchCommand()
        {
            // Declare new branch node variable
            Branch branch = new Branch();
            // Declare variable to hold root
            Root root = this.selectedRootObject;
            // Get selected node
            if ((selectedNodeView.DataContext as NodeViewModel).node == root)
            {
                // Add branch node to selected node view
                (root as Root).AddChild(branch);
            }
            else
            {
                // Add branch node to selected node view
                ((selectedNodeView.DataContext as NodeViewModel).node as Branch).AddChild(branch);
            }
            // Call an update of root by setting to null
            this.selectedRootObject = null;
            // Re update with root
            this.selectedRootObject = root;
        }
        /// <summary>
        /// Raise add global marker method
        /// </summary>
        private void OnRaiseAddGlobalMarker()
        {
            // Check there is a selected node view
            if (selectedNodeView != null)
            {
                // Add default global marker linked to selected node view
                // Declare bool to hold whether global marker for this node is present
                bool markerPresent = false;
                // Get node tag
                string tag = (selectedNodeView.DataContext as NodeViewModel).node.tag;
                // Check a global marker does not exist for this node already
                foreach (Framework.ObservableDictionary<string, object> globalMarker in selectedRootObject.getGlobalLeafCollection())
                {
                    // Check tag
                    if ((string)globalMarker["Tag"] == tag)
                    {
                        // Set present to true
                        markerPresent = true;
                        // Break
                        break;
                    }
                }
                // If marker is not present than add
                if (markerPresent == false)
                {
                    // Declare new global marker
                    Framework.ObservableDictionary<string, object> globalMarker = new Framework.ObservableDictionary<string, object>();
                    // Set tag name
                    globalMarker.Add("Tag", tag);
                    // Add default index value
                    globalMarker.Add("Index", 1);
                    // Add default type value, used to determine how global node is updated, i.e as data or replacing whole node
                    globalMarker.Add("Type", "Data");
                    // Add marker
                    selectedRootObject.AddGlobalLeaf(globalMarker);
                }
            }
        }
        /// <summary>
        /// Raise add node marker method
        /// </summary>
        private void OnRaiseAddNodeMarker()
        {
            // Check there is a selected node view
            if (selectedNodeView != null)
            {
                // Add default node marker linked to selected node view
                // Create a default object
                Object objectMarker = new Object();
                // Declare count integer
                int count = 1;
                // Create default name for marker
                while ((selectedNodeView.DataContext as NodeViewModel).node.ContainsMarker("Marker " + Convert.ToString(count)) == true)
                {
                    // Increment count
                    count++;
                }
                // Add marker
                (selectedNodeView.DataContext as NodeViewModel).node.AddMarker("Marker " + Convert.ToString(count), objectMarker);
            }
        }
        /// <summary>
        /// Raise update selected root object event
        /// </summary>
        public void RaiseUpdateSelectedRootEvent()
        {
            // Raise an update selected root event
            this.aggregator.GetEvent<UpdateSelectedRoot>().Publish(selectedRootObject);
        }
        // Create the OnPropertyChanged method to raise the event
        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
    }
}
