/***********************************************************************
 * 
 *  Copyright (C) 2005-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.
 * 
 ***********************************************************************/


using System;
using System.Text;
using System.Collections;

using Novell.CASA.MiCasa.Common;

namespace sscs.cache
{
    class Secret : ISecret
    {
        private string secretID;
        //private byte[] data;
        string         ePasswd ; // TBD
     
        DateTime        createdTime;
        public DateTime CreatedTime
        {
            get
            {
                return createdTime;
            }
            set
            {
                createdTime = value;
            }
        }

        DateTime        accessedTime;
        public DateTime AccessedTime
        {
            get
            {
                return accessedTime;
            }
            set
            {
                accessedTime = value;
            }
        }

        DateTime        modifiedTime;
        public DateTime ModifiedTime
        {
            get
            {
                return modifiedTime;
            }
            set
            {
                modifiedTime = value;
            }
        }


        private Hashtable htKeyValues = new Hashtable();
        internal Secret()
        {
        }
        
        internal Secret(string secretID)
        {
            this.secretID = secretID;
        }
    
        internal Secret(string secretID, byte[] data)
        {
            this.secretID = secretID;
            //this.data = data;

            // parse the data
            ParseDataBuffer(data);

        }
    
        internal Secret(string secretID, byte[] data, string ePasswd)
        {
            this.secretID = secretID;            
            this.ePasswd = ePasswd;           
            ParseDataBuffer(data);
        }

        internal void ParseDataBuffer(byte[] data)
        {

            if (data != null)
            {
                UTF8Encoding dec = new UTF8Encoding();
                string theData = dec.GetString(data);

                char[] theChars = new char[2];
                theChars[0] = '\n';
                theChars[1] = '\0';
                String[] keyValues = theData.Split(theChars);

                char delimiter = '=';
                foreach (string keyValue in keyValues)
                {
                    int iLocation = keyValue.IndexOf(delimiter);
                    if (iLocation > 0)
                    {
                        String key = keyValue.Substring(0, iLocation);
                        String value = keyValue.Substring(iLocation + 1);

                        if (key != null && value != null)
                            this.SetKeyValue(key, value);
                    }
                }
            }
        }

        internal void SetEpasswd(string value)
        {
            ePasswd = value;
        }

        internal string GetEpasswd()
        {
            return ePasswd;
        }

        public void SetValue(byte[] data)
        {
            this.ModifiedTime = DateTime.Now;
            ParseDataBuffer(data);
        }
    
        public void SetKey(String newkey)
        {
            //Validation TBD
            this.secretID =  newkey;
        }
        
        //Get methods
        public byte[] GetValue(String key)
        {
            //validation TBD
            //if (data == null)
            {
                StringBuilder sb = new StringBuilder();

                IDictionaryEnumerator etor = (IDictionaryEnumerator)this.htKeyValues.GetEnumerator();
                while(etor.MoveNext())
                {
                    string sKey = (string)etor.Key;                    
                    KeyValue kv = (KeyValue)this.htKeyValues[sKey];                                

                    sb.Append(kv.Key);
                    sb.Append("=");
					if (kv.GetValueType() ==(KeyValue.VALUE_TYPE_BINARY))
						sb.Append("BINARY - Do not change");
					else
						sb.Append(kv.GetValue());
                    sb.Append('\n');
                }
                //sb.Append('\0');    
        
                UTF8Encoding enc = new UTF8Encoding();
                this.AccessedTime = DateTime.Now;
                return (enc.GetBytes(sb.ToString()));                
            }
            //else
            //    return data;
        }
        public byte[] GetValue()
        {
            //validation TBD
            this.AccessedTime = DateTime.Now;
            return GetValue(null);
        }
    
        public string GetKey()
        {
            return secretID;
        }

        public void SetKeyValue(string key, string value)
        {
            KeyValue kv;
            if (htKeyValues.Contains(key))
            {
                kv = (KeyValue)htKeyValues[key];
                kv.SetValue(value);
            }
            else
            {
                kv = new KeyValue(key, value);
                htKeyValues.Add(key, kv);
            }
            this.ModifiedTime = DateTime.Now;
        }

		public void SetKeyValue(string key, byte[] baValue)
		{
			KeyValue kv;
			if (htKeyValues.Contains(key))
			{
				kv = (KeyValue)htKeyValues[key];
				kv.SetValue(baValue);
			}
			else
			{
				kv = new KeyValue(key, baValue);
				htKeyValues.Add(key, kv);
			}
			this.ModifiedTime = DateTime.Now;
		}

        public KeyValue GetKeyValue(string key)
        {
            this.AccessedTime = DateTime.Now;
            if (htKeyValues.Contains(key))
            {
                KeyValue kv = (KeyValue)htKeyValues[key];
                return kv;
            }
            else
                return null;
        }

		internal void RemoveAllKeyValuePairs(KeyChain kc)
		{
			if (htKeyValues != null)
			{
                IDictionaryEnumerator enumer = htKeyValues.GetEnumerator();
				while (enumer.MoveNext())
				{
					String key = (String)enumer.Key;
					RemoveKeyValue(kc, key);
                    // refresh enumerator
                    enumer = htKeyValues.GetEnumerator();
				}
			}
		}

        public void RemoveKeyValue(KeyChain kc, string key)
        {			
            if (htKeyValues.Contains(key))
            {
				// remove all reverse links first
				RemoveReverseLinkedKeys(kc, key);								
                htKeyValues.Remove(key);
            }
        }

		private void RemoveReverseLinkedKeys(KeyChain kc, string keyId)
		{
			Hashtable linkedKeys = GetLinkedKeys(keyId);
			if (kc != null && linkedKeys != null)
			{
				IDictionaryEnumerator lkis = linkedKeys.GetEnumerator();
				while (lkis.MoveNext())
				{
					LinkedKeyInfo lki = (LinkedKeyInfo)lkis.Value;
					
					// look up reverse linked key
					Secret secret = kc.GetSecret(lki.GetLinkedSecretID());
					if (secret != null)
					{	
						// look up linked key
						KeyValue kv = secret.GetKeyValue(lki.GetLinkedKeyID());
						kv.RemoveLink(secretID + ":" + keyId);
					}
				}
			}
		}

        public DateTime GetKeyValueCreatedTime(string key)
        {
            if (htKeyValues.Contains(key))
            {
                KeyValue kv = (KeyValue)htKeyValues[key];
                return kv.CreatedTime;
            }
            else
                return (new DateTime(0));
        }

		public Hashtable GetLinkedKeys(string keyId)
		{
			if (htKeyValues.Contains(keyId))
			{
				KeyValue kv = (KeyValue)htKeyValues[keyId];
				return kv.GetLinkedKeys();
			}
			else 
				return null;

		}

        public DateTime GetKeyValueModifiedTime(string key)
        {
            if (htKeyValues.Contains(key))
            {
                KeyValue kv = (KeyValue)htKeyValues[key];
                return kv.ModifiedTime;
            }
            else
                return (new DateTime(0));
        }


        public void MergeSecret(SecretStore store, Secret newSecret)
        {			

            IDictionaryEnumerator etor = (IDictionaryEnumerator)newSecret.htKeyValues.GetEnumerator();
            while(etor.MoveNext())
            {
                string sKey = (string)etor.Key;

                // TODO:  When we sync, we should consider modified time as well
                KeyValue newKV = (KeyValue)newSecret.htKeyValues[sKey];
                this.SetKeyValue(newKV.Key, newKV.GetValue());
            }
            etor = (IDictionaryEnumerator)newSecret.htKeyValues.GetEnumerator();
            while(etor.MoveNext())
            {
                string sKey = (string)etor.Key;
                if(!htKeyValues.Contains(sKey))
                    this.RemoveKeyValue(store.GetKeyChainDefault(), sKey);
            }
        }

        public ArrayList GetKeyList()
        {
            IDictionaryEnumerator etor = (IDictionaryEnumerator)this.htKeyValues.GetEnumerator();
            ArrayList list = new ArrayList();
            if( null == etor )
            {
                return null;
            }
            while(etor.MoveNext())
            {
                 string key = (string)etor.Key;
                 list.Add(key);
            }
            return list;
        }

	internal IDictionaryEnumerator GetKeyValueEnumerator()
        {
            if( htKeyValues != null)
                return htKeyValues.GetEnumerator();
            else
                return null;
        } 
    
		public string ToXML()
        {
            StringBuilder sb = new StringBuilder();

            sb.Append("<SECRET>");

            sb.Append("<ID>");
            sb.Append(secretID);
            sb.Append("</ID>");

            sb.Append("<DATA>");
            // enum the htKeyValues list
            //IDictionaryEnumerator ienum = htKeyValues.GetEnumerator();
            ICollection coll = htKeyValues.Values;

            IDictionaryEnumerator ienum = (IDictionaryEnumerator)coll.GetEnumerator();


            KeyValue kv = (KeyValue)ienum.Current;
            while (kv != null)
            {
                sb.Append(kv.ToXML());
                if (ienum.MoveNext())
                    kv = (KeyValue)ienum.Value;
                else
                    kv = null;
            }
            sb.Append("</DATA>");
            sb.Append("</SECRET>");

            return sb.ToString();
        }
    }
}