// Created with MonoDevelop
//
//    Copyright (C) 2009 Hiperscan GmbH Dresden, Germany
//
//    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 System.Text;

using Hiperscan.Unix;


namespace Hiperscan.SGS.Sgs1900
{
    internal partial class Device0_10 : Device0_0
    {
        protected new class Protocol
        {
            public static readonly int HeaderLength = 4;
            
            public static readonly Parameter FinderLight     = new Parameter('F', 'L', typeof(byte), sizeof(byte));
            public static readonly Parameter FinderWheel     = new Parameter('F', 'P', typeof(byte), sizeof(byte));
            public static readonly Parameter FanControl      = new Parameter('F', 'F', typeof(byte), sizeof(byte));
            public static readonly Parameter FinderPresent   = new Parameter('F', 'D', typeof(byte), sizeof(byte));
            public static readonly Parameter FinderStatusLED = new Parameter('F', 'B', typeof(byte), sizeof(byte));
            public static readonly Parameter FinderButton    = new Parameter('F', 'S', typeof(byte), sizeof(byte));
        
            public class Parameter
            {
                private ushort count;

                public Parameter(char c1, char c2, Type type, int count)
                {
                    this.Type = type;
                    this.count = (ushort)count;
                    this.Word = new Byte[4 + count];
                    
                    this.Word[0] = (byte)c1;
                    this.Word[1] = (byte)c2;
                    this.Word[2] = BitConverter.GetBytes(count)[0];
                    this.Word[3] = BitConverter.GetBytes(count)[1];
                }
                
                public Parameter(Parameter p)
                {
                    this.Type = p.Type;
                    this.count = p.Count;
                    this.Word = (byte[])p.Word.Clone();
                }
                
                public Parameter(byte[] buf, Type type)
                {
                    this.Type = type;
                    this.count = (ushort)(buf[2] + (buf[3] << 8));
                    
                    if (this.count != buf.Length-4)
                        throw new Exception(Catalog.GetString("Unexpected data length."));
                    
                    this.Word = (byte[])buf.Clone();
                }
                
                public bool CheckResponse(Parameter response, bool data_request)
                {
                    if (this.Word[0] != response.Word[0] ||
                        this.Word[1] != response.Word[1])
                        throw new Exception(Catalog.GetString("Unexpected device response (command ID)."));
                    
                    int count = response.Word[2] + (response.Word[3] << 8);

                    if (data_request && count < 1)
                        throw new Exception(Catalog.GetString("Unexpected device response (byte count).)"));
                    
                    if (data_request)
                        return true;
                    
                    if (count != 1)
                        throw new Exception(Catalog.GetString("Unexpected device response (byte count)."));

                    if (response[4] > 1)
                        throw new Exception(Catalog.GetString("Unexpected device response (return value)."));
                    
                    return (response[4] == 1);
                }
                
                public byte ByteValue
                {
                    get
                    {
                        if (this.Type != typeof(byte))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'byte'."));
                        
                        return this.Word[4];
                    }
                    set
                    {
                        if (this.Type != typeof(byte))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'byte'."));
                        
                        this.Word[4] = value;
                    }
                }
                
                public byte ByteValue2
                {
                    get
                    {
                        if (this.Type != typeof(byte))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'byte'."));
                        
                        return this.Word[5];
                    }
                    set
                    {
                        if (this.Type != typeof(byte))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'byte'."));
                        
                        this.Word[5] = value;
                    }
                }
                
                public short ShortValue
                {
                    get
                    {
                        if (this.Type != typeof(short))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'short'."));
                        
                        return BitConverter.ToInt16(this.Word, 4);
                    }
                    set
                    {
                        if (this.Type != typeof(short))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'short'."));
                        
                        this.Word[4] = BitConverter.GetBytes(value)[0];
                        this.Word[5] = BitConverter.GetBytes(value)[1];
                    }
                }

                public ushort UShortValue
                {
                    get
                    {
                        if (this.Type != typeof(ushort))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'ushort'."));
                        
                        return BitConverter.ToUInt16(this.Word, 4);
                    }
                    set
                    {
                        if (this.Type != typeof(ushort))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'ushort'."));
                        
                        this.Word[4] = BitConverter.GetBytes(value)[0];
                        this.Word[5] = BitConverter.GetBytes(value)[1];
                    }
                }
                
                public ushort UShortValue2
                {
                    get
                    {
                        if (this.Type != typeof(ushort))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'ushort'."));
                        
                        return BitConverter.ToUInt16(this.Word, 6);
                    }
                    set
                    {
                        if (this.Type != typeof(ushort))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'ushort'."));
                        
                        this.Word[6] = BitConverter.GetBytes(value)[0];
                        this.Word[7] = BitConverter.GetBytes(value)[1];
                    }
                }
                
                public int IntValue
                {
                    get
                    {
                        if (this.Type != typeof(int))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'int'."));
                        
                        return BitConverter.ToInt32(this.Word, 4);
                    }
                    set
                    {
                        if (this.Type != typeof(int))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'int'."));
                        
                        this.Word[4] = BitConverter.GetBytes(value)[0];
                        this.Word[5] = BitConverter.GetBytes(value)[1];
                        this.Word[6] = BitConverter.GetBytes(value)[2];
                        this.Word[7] = BitConverter.GetBytes(value)[3];
                    }
                }
                
                public uint UIntValue
                {
                    get
                    {
                        if (this.Type != typeof(uint))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'uint'."));
                        
                        return BitConverter.ToUInt32(this.Word, 4);
                    }
                    set
                    {
                        if (this.Type != typeof(uint))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'uint'."));
                        
                        this.Word[4] = BitConverter.GetBytes(value)[0];
                        this.Word[5] = BitConverter.GetBytes(value)[1];
                        this.Word[6] = BitConverter.GetBytes(value)[2];
                        this.Word[7] = BitConverter.GetBytes(value)[3];
                    }
                }
                
                public uint UIntValue2
                {
                    get
                    {
                        if (this.Type != typeof(uint))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'uint'."));
                        
                        return BitConverter.ToUInt32(this.Word, 8);
                    }
                    set
                    {
                        if (this.Type != typeof(uint))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'uint'."));
                        
                        this.Word[8]  = BitConverter.GetBytes(value)[0];
                        this.Word[9]  = BitConverter.GetBytes(value)[1];
                        this.Word[10] = BitConverter.GetBytes(value)[2];
                        this.Word[11] = BitConverter.GetBytes(value)[3];
                    }
                }
                
                public long LongValue
                {
                    get
                    {
                        if (this.Type != typeof(long))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'long'."));
                        
                        return BitConverter.ToInt64(this.Word, 4);
                    }
                    set
                    {
                        if (this.Type != typeof(long))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'long'."));
                        
                        this.Word[4]  = BitConverter.GetBytes(value)[0];
                        this.Word[5]  = BitConverter.GetBytes(value)[1];
                        this.Word[6]  = BitConverter.GetBytes(value)[2];
                        this.Word[7]  = BitConverter.GetBytes(value)[3];
                        this.Word[8]  = BitConverter.GetBytes(value)[4];
                        this.Word[9]  = BitConverter.GetBytes(value)[5];
                        this.Word[10] = BitConverter.GetBytes(value)[6];
                        this.Word[11] = BitConverter.GetBytes(value)[7];
                    }
                }

                public string StringValue
                {
                    get
                    {
                        if (this.Type != typeof(string))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'string'."));

                        return Encoding.Default.GetString(this.Word, Protocol.HeaderLength,
                                                          this.Word.Length - Protocol.HeaderLength);
                    }
                    set
                    {
                        if (this.Type != typeof(String))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'string'."));
                        
                        if (this.count != 0 && value.Length != this.count)
                            throw new Exception(Catalog.GetString("Unexpected string length."));

                        byte[] buf = Encoding.Default.GetBytes(value);
                        Buffer.BlockCopy(buf, 0, this.Word, Protocol.HeaderLength, value.Length);
                    }
                }
                
                public byte[] ByteArray
                {
                    get
                    {
                        if (this.Type != typeof(byte[]))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'byte[]'."));
                        
                        byte[] buf = new byte[this.Word.Length - Protocol.HeaderLength];
                        Buffer.BlockCopy(this.Word, Protocol.HeaderLength, buf, 0, buf.Length);
                        return buf;
                    }
                    set
                    {
                        if (this.Type != typeof(byte[]))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'byte[]'."));
                        
                        if (this.count != 0 && value.Length != this.count)
                            throw new Exception(Catalog.GetString("Parameter data array has unexpected length."));
                        
                        Buffer.BlockCopy(value, 0, this.Word, Protocol.HeaderLength, value.Length);
                    }
                }
                
                public short[] ShortArray
                {
                    get
                    {
                        if (this.Type != typeof(short[]))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'short[]'."));
                        
                        short[] buf = new short[(this.Word.Length - Protocol.HeaderLength) / 2];
                        Buffer.BlockCopy(this.Word, Protocol.HeaderLength, buf, 0, buf.Length*2);
                        return buf;
                    }
                    set
                    {
                        if (this.Type != typeof(short[]))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'short[]'."));
                        
                        if (this.count != 0 && value.Length != this.count / 2)
                            throw new Exception(Catalog.GetString("Parameter data array has unexpected length."));
                        
                        Buffer.BlockCopy(value, 0, this.Word, Protocol.HeaderLength, value.Length*2);
                    }
                }

                public ushort[] UShortArray
                {
                    get
                    {
                        if (this.Type != typeof(ushort[]))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'ushort[]'."));
                        
                        ushort[] buf = new ushort[(this.Word.Length - Protocol.HeaderLength) / 2];
                        Buffer.BlockCopy(this.Word, Protocol.HeaderLength, buf, 0, buf.Length*2);
                        return buf;
                    }
                    set
                    {
                        if (this.Type != typeof(ushort[]))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'ushort[]'."));
                        
                        if (this.count != 0 && value.Length != this.count / 2)
                            throw new Exception(Catalog.GetString("Parameter data array has unexpected length."));
                        
                        Buffer.BlockCopy(value, 0, this.Word, Protocol.HeaderLength, value.Length*2);
                    }
                }

                public int[] IntArray
                {
                    get
                    {
                        if (this.Type != typeof(int[]))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'int[]'."));
                        
                        int[] buf = new int[(this.Word.Length - Protocol.HeaderLength) / 4];
                        Buffer.BlockCopy(this.Word, Protocol.HeaderLength, buf, 0, buf.Length*4);
                        return buf;
                    }
                    set
                    {
                        if (this.Type != typeof(int[]))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'int[]'."));
                    
                        if (this.count != 0 && value.Length != this.count / 4)
                            throw new Exception(Catalog.GetString("Parameter data array has unexpected length."));
                        
                        Buffer.BlockCopy(value, 0, this.Word, Protocol.HeaderLength, value.Length*4);
                    }
                }

                public uint[] UIntArray
                {
                    get
                    {
                        if (this.Type != typeof(uint[]))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'uint[]'."));

                        uint[] buf = new uint[(this.Word.Length - Protocol.HeaderLength) / 4];
                        Buffer.BlockCopy(this.Word, Protocol.HeaderLength, buf, 0, buf.Length*4);
                        return buf;
                    }
                    set
                    {
                        if (this.Type != typeof(uint[]))
                            throw new Exception(Catalog.GetString("Parameter data type is not 'uint[]'."));

                        if (this.count != 0 && value.Length != this.count / 4)
                            throw new Exception(Catalog.GetString("Parameter data array has unexpected length."));

                        Buffer.BlockCopy(value, 0, this.Word, Protocol.HeaderLength, value.Length*4);
                    }
                }

                public byte[] Word { get; private set; }

                public byte[] Query
                {
                    get
                    {
                        byte[] buf = new byte[4];
                        Array.Copy(this.Word, buf, 2);
                        return buf;
                    }
                }

                public bool QueryHasData { get; set; } = false;

                public Type Type { get; }

                public ushort Count
                {
                    get { return this.count; }
                    internal set 
                    { 
                        this.count = value; 
                        byte[] new_word  = new Byte[4 + this.count];
                        new_word[0] = this.Word[0];
                        new_word[1] = this.Word[1];
                        new_word[2] = BitConverter.GetBytes(this.count)[0];
                        new_word[3] = BitConverter.GetBytes(this.count)[1];
                        this.Word = new_word;
                    }
                }
                
                public byte this[int ix]
                {
                    get { return this.Word[ix]; }
                }
                
                public static bool operator==(Parameter lhs, Parameter rhs)
                {
                    if (lhs.Word.Length != rhs.Word.Length)
                        return false;
                    
                    for (int ix=0; ix < lhs.Word.Length; ++ix)
                    {
                        if (lhs.Word[ix] != rhs.Word[ix])
                            return false;
                    }
                    
                    return true;
                }
                
                public static bool operator!=(Parameter lhs, Parameter rhs)
                {
                    return !(lhs == rhs);
                }
                
                public override bool Equals(object o)
                {
                    try
                    {
                        return (this == (Parameter)o);
                    }
                    catch
                    {
                        return false;
                    }
                }
                
                public override int GetHashCode()
                {
                    return this[0].GetHashCode() ^ this[1].GetHashCode();
                }
            }
        }
    }
}
