/*******************************************************************************
* Copyright (C) 2016 Maxim Integrated Products, Inc., All rights Reserved.
* 
* This software is protected by copyright laws of the United States and
* of foreign countries. This material may also be protected by patent laws
* and technology transfer regulations of the United States and of foreign
* countries. This software is furnished under a license agreement and/or a
* nondisclosure agreement and may only be used or reproduced in accordance
* with the terms of those agreements. Dissemination of this information to
* any party or parties not specified in the license agreement and/or
* nondisclosure agreement is expressly prohibited.
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*******************************************************************************
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RPCSupport.Pipelines;
using System.Globalization;

namespace RPCSupport
{
    public class DataLogPipeline : Pipeline
    {
        int page;
        bool isConnected;
        bool endOfLog = false;
        Pipeline proxyPipeline;

        const int  DATALOG_PARAMETER_PAGE = 0x0;
        const int  DATALOG_RESUME_TABLE_PAGE = 0x10;
        const int  DATALOG_DATA_PAGE = 0x12;
        class DataLogPacket
        {
            public int index;
            public char[] buffer;
            public int LengthLeft()
            {
                return buffer.Length - index;
            }
            public char FetchChar()
            {
                char ch = (char)buffer[index++];
                return ch;
            }
        }
        Queue<DataLogPacket> queue = new Queue<DataLogPacket>();
        public void SetProxyPipeline(Pipeline pipeline)
        {
            page = DATALOG_DATA_PAGE;
            isConnected = true;
            proxyPipeline = pipeline;
            endOfLog = false;
        }

        public override string RawRpcCall(string request, bool reply)
        {
            throw new NotImplementedException();
        }

        public override void SendSingleByte(char ch)
        {
            throw new NotImplementedException();
        }

        public override void Discard()
        {
            throw new NotImplementedException();
        }

        public override string RawRpcCallBinary(string request, bool reply, out bool allOnes)
        {
            throw new NotImplementedException();
        }

        public override bool IsConnected()
        {
            return true;
        }

        public override string ReadString()
        {
            throw new NotImplementedException();
        }

        public override int Read(char[] buffer, int offset, int count)
        {
            int numberRead = 0;
            char[] currentBuffer = ReadDataLog(buffer, offset, count, out numberRead);
            //String returnStr = "";
            //bool allOnes;
            //returnStr = proxyPipeline.CallBinary("S25FS512", "ReadPagesBinary", out allOnes, new String[] { page.ToString("X2"), page.ToString("X2") });
            return numberRead;
        }

        private char[] ReadDataLog(char[] buffer, int offset, int count, out int numberRead)
        {
            // if queue empty then read from datalog
            if (queue.Count == 0)
            {
                QueuePage();
            }
             // get the log packet at the top of the queue
            DataLogPacket currentPacket = queue.Peek();
           // check if we need to get another page queued if more buffer count is needed
            bool extraQueued = false;
            int currentPacketIndex = currentPacket.index;
            int currentPacketLengthLeft = currentPacket.LengthLeft();
            if (currentPacket.LengthLeft() < count)
            {
                QueuePage();
                extraQueued = true;
            }
            // get data from the queue
            for (int i = 0; i < count; i++)
            {
                buffer[i] = currentPacket.FetchChar();
                // if there is no data left in this packet then get the next packet
                if (currentPacket.LengthLeft() == 0 && i != (count - 1))
                {
                    if (queue.Count != 2)
                    {
                    }
                    queue.Dequeue();
                    currentPacket = queue.Peek();
                }
            }
            // if this is the last buffer then return count of 0 to end the stream
            //if (IsAllFF(currentPacket.buffer) == true) count = 0;
            // if the DataLogPacket is exhasted then dequeue it, we are finished with it
            if (currentPacket.LengthLeft() == 0)
            {
                queue.Dequeue();
            }
            if (endOfLog == true)
            {
                count = 0;
            }
            numberRead = count;
            return buffer;
        }

        private void QueuePage()
        {
            byte[] buf = ReadPageFromFlash(page);
            if (page >= 0x18F)
            {
                //endOfLog = true;
            }
            if (IsAllFF(buf) == true)
            {
                endOfLog = true;
            }
            page++;
            char[] charArray = BinToCharArray(buf);
            DataLogPacket packet = new DataLogPacket();
            packet.buffer = charArray;
            packet.index = 0;
            queue.Enqueue(packet);
        }

        private char[] BinToCharArray(byte[] buf)
        {
            int val;
            int val1;
            int val2;
            int val3;
            int val4;
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < buf.Length / 4; i++) {
                val1 = (int)buf[i*4 + 3] << 24;
                val2 = (int)buf[i*4 + 2] << 16;
                val3 = (int)buf[i*4 + 1] << 8;
                val4 = (int)buf[i*4 + 0] << 0;
                val = val1 + val2 + val3 + val4;
                sb.Append(String.Format("{0:X} ", val));
            }
            char[] chars = sb.ToString().ToCharArray();
            return chars;
        }

        private byte[] ReadPageFromFlash(int pageToRead)
        {
            String returnStr = "";
            bool allOnes;
            returnStr = proxyPipeline.CallBinary("S25FS512", "ReadPagesBinary", out allOnes, new String[] { pageToRead.ToString("X2"), pageToRead.ToString("X2") });
            byte[] array = LineToArray(returnStr);
            return array;
        }

        private bool IsAllFF(byte[] buffer)
        {
            for (int i = 0; i < buffer.Length; i++)
            {
                if (buffer[i] != 0xFF) return false;
            }
            return true;
        }

        private byte[] LineToArray(String hexStr)
        {
            byte val;
            List<byte> intList = new List<byte>();
            String[] strings = hexStr.Split(new char[] { ' ' });
            foreach (String str in strings)
            {
                if (byte.TryParse(str, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out val))
                {
                    intList.Add(val);
                }
                else
                {
                    throw new Exception(String.Format("Error In hex string \"{0}\" in \"{1}\"", str, hexStr));
                }
            }
            return intList.ToArray();
        }
    }
}
