// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// 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 THE
// AUTHORS OR COPYRIGHT HOLDERS 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.

using System;
using System.IO;
using System.Linq;

namespace WPinternals
{
    internal class QualcommDownload
    {
        private QualcommSerial Serial;

        public QualcommDownload(QualcommSerial Serial)
        {
            this.Serial = Serial;
        }

        public bool IsAlive()
        {
            try
            {
                Serial.SendCommand(new byte[] { 0x06 }, new byte[] { 0x02 });
                return true;
            }
            catch
            {
                return false;
            }
        }

        public void SendToPhoneMemory(UInt32 Address, Stream Data, UInt32 Length = UInt32.MaxValue)
        {
            long Remaining;
            if (Length > (Data.Length - Data.Position))
                Remaining = Data.Length - Data.Position;
            else
                Remaining = Length;
            UInt32 CurrentLength;
            byte[] Buffer = new byte[0x107];
            Buffer[0] = 0x0F;
            System.Buffer.BlockCopy(BitConverter.GetBytes((UInt16)0x100).Reverse().ToArray(), 0, Buffer, 5, 2); // Length is in Big Endian
            UInt32 CurrentAddress = Address;
            while (Remaining > 0)
            {
                System.Buffer.BlockCopy(BitConverter.GetBytes(CurrentAddress).Reverse().ToArray(), 0, Buffer, 1, 4); // Address is in Big Endian

                if (Remaining >= 0x100)
                    CurrentLength = 0x100;
                else
                    CurrentLength = (UInt32)Remaining;
                CurrentLength = (UInt32)(Data.Read(Buffer, 7, (int)CurrentLength));
                Serial.SendCommand(Buffer, new byte[] { 0x02 });

                CurrentAddress += CurrentLength;
                Remaining -= CurrentLength;
            }
        }

        public void SendToPhoneMemory(UInt32 Address, byte[] Data, UInt32 Offset = 0, UInt32 Length = UInt32.MaxValue)
        {
            long Remaining;
            if (Offset > (Data.Length - 1))
                throw new ArgumentException("Wrong offset");
            if (Length > (Data.Length - Offset))
                Remaining = Data.Length - Offset;
            else
                Remaining = Length;
            UInt32 CurrentLength;
            UInt32 CurrentOffset = Offset;
            byte[] Buffer = new byte[0x107];
            UInt32 CurrentAddress = Address;
            byte[] CurrentBytes;
            while (Remaining > 0)
            {

                if (Remaining >= 0x100)
                {
                    CurrentLength = 0x100;
                    CurrentBytes = Buffer;
                }
                else
                {
                    CurrentLength = (UInt32)Remaining;
                    CurrentBytes = new byte[CurrentLength + 7];
                }
                CurrentBytes[0] = 0x0F;
                System.Buffer.BlockCopy(BitConverter.GetBytes(CurrentAddress).Reverse().ToArray(), 0, CurrentBytes, 1, 4); // Address is in Big Endian
                System.Buffer.BlockCopy(BitConverter.GetBytes((UInt16)CurrentLength).Reverse().ToArray(), 0, CurrentBytes, 5, 2); // Length is in Big Endian
                System.Buffer.BlockCopy(Data, (int)CurrentOffset, CurrentBytes, 7, (int)CurrentLength);

                Serial.SendCommand(CurrentBytes, new byte[] { 0x02 });

                CurrentAddress += CurrentLength;
                CurrentOffset += CurrentLength;
                Remaining -= CurrentLength;
            }
        }

        public void StartBootloader(UInt32 Address)
        {
            byte[] Buffer = new byte[5];
            Buffer[0] = 0x05;
            System.Buffer.BlockCopy(BitConverter.GetBytes(Address).Reverse().ToArray(), 0, Buffer, 1, 4); // Address is in Big Endian
            Serial.SendCommand(Buffer, new byte[] { 0x02 });
        }

        // Reset interface. Interface becomes unresponsive.
        public void Reset()
        {
            Serial.SendCommand(new byte[] { 0x0A }, new byte[] { 0x02 });
        }

        // This also resets interface. This does not actually reboot the phone. The interface becomes unresponsive.
        public void Shutdown()
        {
            Serial.SendCommand(new byte[] { 0x0E }, new byte[] { 0x02 });
        }

        // This command only works on 9008 interface.
        public byte[] GetRKH()
        {
            byte[] Response = Serial.SendCommand(new byte[] { 0x18 }, new byte[] { 0x18, 0x01, 0x00 });
            byte[] Result = new byte[0x20];
            Buffer.BlockCopy(Response, 3, Result, 0, 0x20);
            return Result;
        }

        public void CloseSerial()
        {
            Serial.Close();
        }
    }
}
