// 
// Hiero
// Copyright (c) 2015  Barry Block 
// 
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later
// version. 
// 
// This program is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE.  See the GNU General Public License for more details. 
// 
// You should have received a copy of the GNU General Public License along with
// this program.  If not, see <http://www.gnu.org/licenses/>. 
//

using System;
using Gtk;

namespace Hiero
{
   // Implements Hiero's custom TreeView widget.
   public class OutlineView : Gtk.TreeView
   {
      public OutlineView(TreeStore model) : base(model)
      {
      }

      // Copies the source node (and its sub-nodes) beneath the
      // destination parent and returns the new node (dstNode).
      public static void CopyNode(TreeStore model, TreeIter srcNode, TreeIter dstParent, out TreeIter dstNode)    
      {
         // Process the node:

         string nodeText = (string) model.GetValue(srcNode, 0);
         bool nodeMatched = (bool) model.GetValue(srcNode, 1);
         dstNode = model.AppendValues(dstParent, nodeText, nodeMatched);

         // Process the child nodes:

         int NoOfChildren = model.IterNChildren(srcNode);
         for (int i = 0; i < NoOfChildren; i++)
         {
            TreeIter srcChild;
            TreeIter dummyNode;
            model.IterNthChild(out srcChild, srcNode, i);
            CopyNode(model, srcChild, dstNode, out dummyNode); 
         }
      }

      protected override bool OnKeyPressEvent(Gdk.EventKey evnt)
      {
         Gdk.Key theKey = evnt.Key;
         char theChar = (char) evnt.KeyValue;
         //Console.WriteLine("KeyValue: " + evnt.KeyValue);

         bool controlPressed = (evnt.State & Gdk.ModifierType.ControlMask) != 0;   
         bool altPressed = (evnt.State & Gdk.ModifierType.Mod1Mask) != 0;            
         bool shiftPressed = (evnt.State & Gdk.ModifierType.ShiftMask) != 0;
         
         // Get selected node:

         TreeIter selectedNode;
         TreeStore model = (TreeStore) this.Model;
         this.Selection.GetSelected(out selectedNode);

         // LEFT: Positioning to parent node while collapsing its children.
         if (!altPressed && !controlPressed && !shiftPressed && (theKey == Gdk.Key.Left))
         {
            // Get the selected node's parent:

            TreeIter parentNode;
            bool Ok = model.IterParent(out parentNode, selectedNode);
   
            if (Ok)
            {
               // Got the parent. Collapse the current level and select it:
      
               TreePath thePath = model.GetPath(parentNode); 
               this.CollapseRow(thePath); 
               this.SetCursor(thePath, this.Columns[0], false); 
               this.GrabFocus(); 
               return true;
            }
         }

         // RIGHT: Expanding / positioning to first child node.
         if (!altPressed && !controlPressed && !shiftPressed && (theKey == Gdk.Key.Right))
         {
            if (model.IterHasChild(selectedNode))
            {
               // The selected node has children. Get the first one:

               TreeIter childNode;
               bool Ok = model.IterNthChild(out childNode, selectedNode, 0);
               if (Ok) 
               {
                  // Expand the children and select the first child:

                  TreePath thePath = model.GetPath(selectedNode);
                  this.ExpandToPath(thePath);

                  thePath = model.GetPath(childNode);
                  this.SetCursor(thePath, this.Columns[0], false);
                  return true;
               }
            }
         }

         // ALT-UP: Positioning to prior sibling.
         if (altPressed && !controlPressed && !shiftPressed && (theKey == Gdk.Key.Up))
         {
            // Get the prior sibling:

            //TreeIter priorNode = selectedNode;
            //bool Ok = model.IterPrevious(ref priorNode);
            TreeIter priorNode = Command.IterPrevious(model, selectedNode);
 
            //if (Ok)
            if (!priorNode.Equals(TreeIter.Zero))
            {
               // Got the prior node. Select it:

               this.Selection.SelectIter(priorNode); 
               return true;
            }
         }

         // ALT-DOWN: Positioning to next sibling.
         if (altPressed && !controlPressed && !shiftPressed && (theKey == Gdk.Key.Down))
         {
            // Get the next sibling:

            TreeIter nextNode = selectedNode;
            bool Ok = model.IterNext(ref nextNode);

            if (Ok)
            {
               // Got the next node. Select it:

               this.Selection.SelectIter(nextNode); 
               return true;
            }
         }

         bool ctrlHome = !altPressed && controlPressed && !shiftPressed && (theKey == Gdk.Key.Home);
         bool ctrlEnd = !altPressed && controlPressed && !shiftPressed && (theKey == Gdk.Key.End);

         // CTRL-HOME or CTRL-END: Positioning to top/bottom of outline.
         if (ctrlHome || ctrlEnd)
         {
            // Call the base handler so that it can position the cursor to the top/bottom of the outline:

            base.OnKeyPressEvent(evnt);

            // Select the node having the cursor:

            TreePath thePath;
            TreeViewColumn theColumn;
            this.GetCursor(out thePath, out theColumn);
            this.SetCursor(thePath, this.Columns[0], false);
            return true;
         }

         bool ctrlUp = !altPressed && controlPressed && !shiftPressed && (theKey == Gdk.Key.Up);
         bool ctrlDown = !altPressed && controlPressed && !shiftPressed && (theKey == Gdk.Key.Down);

         // CTRL-UP or CTRL-DOWN: Moving node up/down.  
         if (ctrlUp || ctrlDown)
         {
            return true;
         }

         // ENTER/RETURN: Disabled row activation via pressing ENTER/RETURN key.
         if (!altPressed && !controlPressed && !shiftPressed && (theKey == Gdk.Key.Return))
         {
            return true;
         }

         // SPACEBAR: Disabled row activation via pressing SPACEBAR key.
         if (!altPressed && !controlPressed && !shiftPressed && (theKey == Gdk.Key.space))
         {
            return true;
         }

         // The key combination wasn't handled above so give the base class a "whack" at it:

         return base.OnKeyPressEvent(evnt);
      }
   }
}
