/* -*- Mode: C#; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Manticore.
 *
 * The Initial Developer of the Original Code is
 * Silverstone Interactive.
 * Portions created by the Initial Developer are Copyright (C) 2001
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Ben Goodger <ben@netscape.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

namespace Silverstone.Manticore.Core
{
  using System;
  using System.Collections;

  using System.IO;
  using System.Xml;
  using System.Text;

  public class Preferences 
  {
    XmlDocument mDefaultsDocument;
    XmlDocument mPrefsDocument;

    public Preferences()
    {
      mDefaultsDocument = new XmlDocument();
      mPrefsDocument = new XmlDocument();
    }

    public void InitializeDefaults()
    {
      // Do we ever want to support multiple defaults files? For now, no.
      // XXX need a better place for this file.
      ReadDocument(@"defaults\default-prefs.xml", mDefaultsDocument);
    }

    private void ReadDocument(String aFile, XmlDocument aDocument)
    {
      XmlTextReader reader = new XmlTextReader(aFile);
      reader.WhitespaceHandling = WhitespaceHandling.None;
      reader.MoveToContent();
      String xml = reader.ReadOuterXml();
      reader.Close();
      aDocument.LoadXml(xml);
    }

    // Called once at Startup
    public void LoadPreferencesFile(String aFile)
    {
      ReadDocument(aFile, mPrefsDocument);

      mPrefsDocument.NodeChanged += new XmlNodeChangedEventHandler(OnNodeChanged);
      mPrefsDocument.NodeRemoved += new XmlNodeChangedEventHandler(OnNodeRemoved);
      mPrefsDocument.NodeInserted += new XmlNodeChangedEventHandler(OnNodeInserted);
    }

    public void LoadUserPreferences()
    {
      String manticoreAppData = FileLocator.GetManticorePath("AppData");
      String prefPath = FileLocator.GetManticorePath("UserPrefs");
      try {
        LoadPreferencesFile(prefPath);
      }
      catch (XmlException) {
        // Something went wrong, we'll just assume a malformed or non-existant 
        // preferences file, blow it away and insert a new one. Could potentially
        // be dangerous. 
        try {
          File.Copy(@"defaults\user-prefs.xml", prefPath, true);
        }
        catch (DirectoryNotFoundException) {
          Directory.CreateDirectory(manticoreAppData);
          File.Copy(@"defaults\user-prefs.xml", prefPath, true);
        }
        LoadPreferencesFile(prefPath);
      }
    }

    public void FlushUserPreferences()
    {
      FlushPreferencesFile(FileLocator.GetManticorePath("UserPrefs"));
    }

    public void OnNodeChanged(object sender, XmlNodeChangedEventArgs e)
    {
    }

    public void OnNodeRemoved(object sender, XmlNodeChangedEventArgs e)
    {
    }

    public void OnNodeInserted(object sender, XmlNodeChangedEventArgs e)
    {
    }

    public void FlushPreferencesFile(String aFile)
    {
      UTF8Encoding enc = new UTF8Encoding();
      XmlTextWriter writer = new XmlTextWriter(aFile, enc);
      writer.Formatting = Formatting.Indented;
      mPrefsDocument.WriteTo(writer);
      writer.Flush();
    }
 
    //
    // The Manticore preferences file takes the following (XML) format:
    // 
    // <preferences>
    //   <foopy>
    //     <noopy type="int" value="42">
    //       <noo type="bool" value="true"/>
    //       <goo type="string" value="goats"/>
    //     </noopy>
    //   </foopy>
    // </preferences>
    //
    // where this maps to preferences called:
    // foopy.noopy (int pref, value 42)
    // foopy.noopy.noo (bool pref, value true);
    // foopy.noopy.goo (string pref, value "goats");
    //
    private XmlElement CreateBranch(String aPrefName) 
    {
      String[] names = aPrefName.Split('.');
      XmlElement elt = mPrefsDocument.DocumentElement;
      for (int i = 0; i < names.Length; ++i)
        elt = CreateBranch(names[i], elt);
      return elt;
    }

    private XmlElement CreateBranch(String aBranchName, XmlElement aRoot) 
    {
      XmlElement elt = GetBranch(aBranchName, aRoot);
      if (elt == null) {
        elt = mPrefsDocument.CreateElement(aBranchName);
        aRoot.AppendChild(elt);
      }
      return elt;
    }

    private XmlElement GetBranchElement(String aBranchName) 
    {
      String[] names = aBranchName.Split('.');
      XmlElement elt = mPrefsDocument.DocumentElement;
      for (int i = 0; i < names.Length && elt != null; ++i)
        elt = GetBranch(names[i], elt);
      
      // The preference wasn't found in the user preferences
      // file, look in the default preferences file. 
      if (elt == null) {
        elt = mDefaultsDocument.DocumentElement;
        for (int i = 0; i < names.Length; ++i)
          elt = GetBranch(names[i], elt);
      }

      return elt;
    }

    private XmlElement GetBranch(String aBranchName, XmlElement aRoot) 
    {
      // First, check to see if the specified root already has a branch
      // specified. If it exists, hand that root back. 
      int childCount = aRoot.ChildNodes.Count;
      for (int i = 0; i < childCount; ++i) {
        if (aRoot.ChildNodes[i].LocalName == aBranchName)
          return aRoot.ChildNodes[i] as XmlElement;
      }
      return null;
    }

    public PrefBranch GetBranch(String aBranchName)
    {
      return new PrefBranch(aBranchName, GetBranchElement(aBranchName));
    }

    public void RemoveBranch(String aBranchName) 
    {
      XmlElement elt = GetBranchElement(aBranchName);
      XmlNode parent = elt.ParentNode;
      parent.RemoveChild(elt);
      while (parent != null && 
             parent != (mPrefsDocument.DocumentElement as XmlNode)) {
        if (!parent.HasChildNodes) {
          parent.ParentNode.RemoveChild(parent);
          parent = parent.ParentNode;
        }
        else 
          break;
      }
    }

    public bool GetBoolPref(String aPrefName)
    {
      XmlElement elt = GetBranchElement(aPrefName);
      return elt != null ? elt.GetAttribute("value") == "true" : false;
    }

    public void SetBoolPref(String aPrefName, bool aPrefValue)
    {
      XmlElement childElt = CreateBranch(aPrefName);
      if (childElt != null) 
        childElt.SetAttribute("value", aPrefValue ? "true" : "false");
    }

    public void RemovePref(String aPrefName)
    {
      RemoveBranch(aPrefName);
    }

    public int GetIntPref(String aPrefName)
    {
      XmlElement elt = GetBranchElement(aPrefName);
      return elt != null ? Int32.Parse(elt.GetAttribute("value")) : 0;
    }

    public void SetIntPref(String aPrefName, int aPrefValue)
    {
      XmlElement elt = CreateBranch(aPrefName);
      if (elt != null) {
        Object o = aPrefValue;
        elt.SetAttribute("value", o.ToString());
      }
    }

    public String GetStringPref(String aPrefName)
    {
      XmlElement elt = GetBranchElement(aPrefName);
      return elt != null ? elt.GetAttribute("value") : "";
    }

    public void SetStringPref(String aPrefName, String aPrefValue)
    {
      XmlElement elt = CreateBranch(aPrefName);
      if (elt != null)
        elt.SetAttribute("value", aPrefValue);
    }

    public static String ResolvePref(XmlElement aElement)
    {
      String rv = aElement.LocalName;
      XmlElement temp = aElement;
      while (true) {
        temp = temp.ParentNode as XmlElement;
        if (temp == null || temp.LocalName == "preferences")
          break;
        rv = temp.LocalName + "." + rv;
      }
      return rv;
    }
  }

  public class PrefBranch : IEnumerator
  {
    //private XmlElement mRoot;
    private String mBranchName;
    private XmlElement mRoot;
    private XmlElement mCurrent = null;

    public PrefBranch(String aPrefBranchName, XmlElement aBranchRoot)
    {
      mBranchName = aPrefBranchName;
      mRoot = aBranchRoot;
    }

    public Object Current
    {
      get 
      {
        return Preferences.ResolvePref(mCurrent) as Object; 
      }
    }

    public bool MoveNext()
    {
      if (mCurrent != null) 
      {
        if (mCurrent.NextSibling != null) 
          mCurrent = mCurrent.NextSibling as XmlElement;
        else 
          return false;
      }
      else 
        mCurrent = mRoot.FirstChild as XmlElement;
      return true;
    }

    public void Reset()
    {
      mCurrent = mRoot.FirstChild as XmlElement;
    }
  }
}