/***********************************************************************
 *
 *  Copyright (C) 2006 Novell, Inc. All Rights Reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; version 2.1
 *  of the License.
 *
 *  This library 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
 *  Library Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, Novell, Inc.
 *
 *  To contact Novell about this file by physical or electronic mail,
 *  you may find current contact information at www.novell.com.
 *
 *  Author: Juan Carlos Luciani <jluciani@novell.com>
 *
 ***********************************************************************/

package com.novell.casa.authtoksvc;

import org.apache.xerces.parsers.DOMParser;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.Element;

import java.io.*;
import java.io.File;
import java.io.FileInputStream;

/**
 *
 * Class for the creation and editing of settings files.
 *
 */
public final class SettingsFileUtil
{
   /**
    * Gets document for the specified settings file.
    *
    * @param filePath   Path to the settings file.
    * @return           Document representation of the settings file.
    */
   private static Document getSettingsFileDoc(String filePath)
   {
      Document doc;
      FileInputStream inStream = null;

      try
      {
         // Get an input stream to read from settings file
         File f = new File(filePath);
         inStream = new FileInputStream(f);
         InputSource source = new InputSource(inStream);

         DOMParser parser = new DOMParser();
         parser.parse(source);
         doc = parser.getDocument();
      }
      catch (FileNotFoundException e)
      {
         System.err.println("Settings file " + filePath + " not found");
         doc = null;
      }
      catch (SecurityException e)
      {
         System.err.println("SecurityException accessing " + filePath);
         doc = null;
      }
      catch (IOException e)
      {
         System.err.println("IOException accessing " + filePath + " Exception=" + e.toString());
         doc = null;
      }
      catch (SAXException e)
      {
         System.err.println("Settings file " + filePath + " format error");
         doc = null;
      }
      catch (Exception e)
      {
         System.err.println("Settings file " + filePath + " Exception=" + e.toString());
         doc = null;
      }
      finally
      {
         if (inStream != null)
         {
            try
            {
               inStream.close();
            }
            catch (IOException e)
            {
               // Do nothing
            }
         }
      }

      return doc;
   }

   /**
    * Gets a starting settings document.
    *
    * @param settings   String with starting settings document.
    * @return           Starting settings document.
    */
   private static Document getSettingsDoc(String settings)
   {
      Document doc = null;
      StringReader reader = null;

      try
      {
         reader = new StringReader(settings);
         InputSource source = new InputSource(reader);

         DOMParser parser = new DOMParser();
         parser.parse(source);
         doc = parser.getDocument();
      }
      catch (Exception e)
      {
        System.err.println("Program error, exception: " + e.toString());
      }
      finally
      {
         if (reader != null)
         {
            try
            {
               reader.close();
            }
            catch (Exception e)
            {
               // Do nothing
            }
         }
      }

      return doc;
   }

   /**
    * List all of the settings present in the specified file.
    *
    * @param filePath`  Path to the settings file.
    * @return           True if the operation is successfully performed.
    */
   public static boolean performListOperation(String filePath)
   {
      boolean opPerformed = false;

      // List the settings present in the file
      Document doc = getSettingsFileDoc(filePath);
      if (doc != null)
      {
         // Go through the elements of the document
         //
         // IMPORTANT WARNING - Do not change the output format without changing utilities
         // that depend on it such as the yast2-ats module.
         Element root = doc.getDocumentElement();
         Node child;
         Node next = root.getFirstChild();
         while ((child = next) != null)
         {
            next = child.getNextSibling();
            if (child.getNodeType() == Node.ELEMENT_NODE)
            {
               System.out.println(child.getLocalName() + "=" + child.getTextContent());
            }
         }

         opPerformed = true;
      }

      return opPerformed;
   }

   /**
    * Create settings file.
    *
    * @param filePath   Path to the settings file.
    * @param settings   String containing an settings document.
    * @return           True if the operation is successfully performed.
    */
   public static boolean performCreateOperation(String filePath,
                                                String settings)
   {
      boolean opPerformed = false;

      // create a settings file
      Document doc = getSettingsDoc(settings);
      if (doc != null)
      {
         FileOutputStream out = null;

         try
         {
            File f = new File(filePath);
            boolean createStatus = f.createNewFile();
            if (createStatus)
            {
               out = new FileOutputStream(f);
               OutputFormat format = new OutputFormat(doc);
               XMLSerializer serializer = new XMLSerializer(out, format);
               serializer.serialize(doc.getDocumentElement());

               opPerformed = true;
            }
            else
            {
               System.err.println("File " + filePath + " already exists");
            }
         }
         catch (IOException e)
         {
            System.err.println("Error creating file " + filePath + ", exception: " + e.toString());
         }
         catch (SecurityException e)
         {
            System.err.println("SecurityException creating " + filePath);
         }
         finally
         {
            if (out != null)
            {
               try
               {
                  out.flush();
                  out.close();
               }
               catch (IOException e)
               {
                  // Do nothing
               }
            }
         }
      }

      return opPerformed;
   }

   /**
    * Gets value of the specified setting in the specified settings file.
    *
    * @param filePath         Path to the settings file.
    * @param setting          Name of the setting being queried.
    * @param settingVerifier  Setting verifier object.
    * @return                 True if the operation is successfully performed.
    */
   public static boolean performGetOperation(String filePath,
                                              String setting,
                                              IVerifySetting settingVerifier)
   {
      boolean opPerformed = false;

      // Validate the setting name specified
      if (settingVerifier.validSetting(setting))
      {
         // Get settings present in the file
         Document doc = getSettingsFileDoc(filePath);
         if (doc != null)
         {
            // Go through the elements of the document until
            // we find the one specified.
            //
            // IMPORTANT WARNING - Do not change the output format without changing utilities
            // that depend on it such as the yast2-ats module.
            Element root = doc.getDocumentElement();
            Node child;
            Node next = root.getFirstChild();
            boolean settingFound = false;
            while ((child = next) != null
                   && settingFound == false)
            {
               next = child.getNextSibling();
               if (child.getNodeType() == Node.ELEMENT_NODE
                   && child.getLocalName().equalsIgnoreCase(setting))
               {
                  System.out.println(child.getLocalName() + "=" + child.getTextContent());
                  settingFound = true;
               }
            }

            if (settingFound == false)
               System.out.println("Not set");

            opPerformed = true;
         }
      }

      return opPerformed;
   }

   /**
    * Sets the specified setting in the specified settings file.
    *
    * @param filePath         Path to the settings file.
    * @param setting``        Name of the setting to be set.
    * @param value            Value to be assigned to the setting.
    * @param settingVerifier  Setting verifier object.
    * @return                 True if the operation is successfully performed.
    */
   public static boolean performSetOperation(String filePath,
                                             String setting,
                                             String value,
                                             IVerifySetting settingVerifier)
   {
      boolean opPerformed = false;

      // Validate specified setting name and value
      String formalSetting;
      if ((formalSetting = settingVerifier.validSettingNameAndValue(setting, value)) != null)
      {
         // Get settings present in the file
         Document doc = getSettingsFileDoc(filePath);
         if (doc != null)
         {
            // Go through the elements of the document until
            // we find the one specified.
            Element root = doc.getDocumentElement();
            Node child;
            Node next = root.getFirstChild();
            boolean settingFound = false;
            while ((child = next) != null
                   && settingFound == false)
            {
               next = child.getNextSibling();
               if (child.getNodeType() == Node.ELEMENT_NODE
                   && child.getLocalName().equalsIgnoreCase(setting))
               {
                  // Change the value of the setting
                  child.setTextContent(value);
                  settingFound = true;
               }
            }

            if (settingFound == false)
            {
               try
               {
                  Element element = doc.createElement(formalSetting);
                  element.setTextContent(value);
                  root.appendChild(element);
               }
               catch (Exception e)
               {
                  System.err.println("Exception caught " + e.toString());
               }
            }

            // Update the file after removing the text nodes
            FileOutputStream out = null;
            try
            {
               // Remove text nodes
               next = root.getFirstChild();
               while ((child = next) != null)
               {
                  next = child.getNextSibling();
                  if (child.getNodeType() == Node.TEXT_NODE)
                  {
                     // Remove the node
                     root.removeChild(child);
                  }
               }

               // Update file
               File f = new File(filePath);
               out = new FileOutputStream(f);
               OutputFormat format = new OutputFormat(doc);
               XMLSerializer serializer = new XMLSerializer(out, format);
               serializer.serialize(doc.getDocumentElement());

               opPerformed = true;
            }
            catch (IOException e)
            {
               System.err.println("Error writing to file " + filePath + ", exception: " + e.toString());
            }
            catch (SecurityException e)
            {
               System.err.println("SecurityException writting to file " + filePath);
            }
            finally
            {
               if (out != null)
               {
                  try
                  {
                     out.flush();
                     out.close();
                  }
                  catch (IOException e)
                  {
                     // Do nothing
                  }
               }
            }
         }
      }

      return opPerformed;
   }

   /**
    * Remove specified setting from the specified settings file.
    *
    * @param filePath         Path to the settings file.
    * @param setting          Name of the setting to be removed.
    * @param settingVerifier  Setting verifier object.
    * @return                 True if the operation is successfully performed.
    */
   public static boolean performRemoveOperation(String filePath,
                                                String setting,
                                                IVerifySetting settingVerifier)
   {
      boolean opPerformed = false;

      // Validate specified setting name
      if (settingVerifier.validSetting(setting))
      {
         // Get settings present in the file
         Document doc = getSettingsFileDoc(filePath);
         if (doc != null)
         {
            // Go through the elements of the document until
            // we find the one specified.
            Element root = doc.getDocumentElement();
            Node child;
            Node next = root.getFirstChild();
            boolean settingFound = false;
            while ((child = next) != null
                   && settingFound == false)
            {
               next = child.getNextSibling();
               if (child.getNodeType() == Node.ELEMENT_NODE
                   && child.getLocalName().equalsIgnoreCase(setting))
               {
                  // Remove the element from the document
                  root.removeChild(child);
                  settingFound = true;

                  // Update the file after removing the text nodes
                  FileOutputStream out = null;
                  try
                  {
                     // Remove text nodes
                     next = root.getFirstChild();
                     while ((child = next) != null)
                     {
                        next = child.getNextSibling();
                        if (child.getNodeType() == Node.TEXT_NODE)
                        {
                           // Remove the node
                           root.removeChild(child);
                        }
                     }

                     // Update file
                     File f = new File(filePath);
                     out = new FileOutputStream(f);
                     OutputFormat format = new OutputFormat(doc);
                     XMLSerializer serializer = new XMLSerializer(out, format);
                     serializer.serialize(doc.getDocumentElement());

                     opPerformed = true;
                  }
                  catch (IOException e)
                  {
                     System.err.println("Error writing to file " + filePath + ", exception: " + e.toString());
                  }
                  catch (SecurityException e)
                  {
                     System.err.println("SecurityException writting to file " + filePath);
                  }
                  finally
                  {
                     if (out != null)
                     {
                        try
                        {
                           out.flush();
                           out.close();
                        }
                        catch (IOException e)
                        {
                           // Do nothing
                        }
                     }
                  }
               }
            }

            if (settingFound == false)
            {
               // Succeed anyway
               opPerformed = true;
            }
         }
      }

      return opPerformed;
   }
}
