using System;
using HWModules;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

namespace HWModules
{

    public class SMBus : SMBusController
    {
        private SMBus() : base(new I2CDevice.Configuration(0x16 >> 1, 20), 6)
        {

        }

        private static SMBus instance;

        public static SMBus Instance {
            get {
                return instance ?? (instance = new SMBus());
            }
        }


    }
    
    public class SMBusController : I2CPortBase
    {
        private I2CDevice.I2CReadTransaction _rt;
        private I2CDevice.I2CWriteTransaction _wt;

        public SMBusController(I2CDevice.Configuration config, int slotnumber = 0) : base(config, slotnumber)
        {
        }

#region Read Functions

        public ushort ReadRemainingCapAlarm() { return ReadWord(Cmd.RemainingCapAlarm); }
        public ushort ReadRemainingTimeAlarm() { return ReadWord(Cmd.RemainingTimeAlarm); }
        public ushort ReadBatteryMode() { return ReadWord(Cmd.BatteryMode); }
        public ushort ReadAtRate() { return ReadWord(Cmd.AtRate); }
        public ushort ReadAtRateTimeToFull() { return ReadWord(Cmd.AtRateTimeToFull); }
        public ushort ReadAtRateTimeToEmpty() { return ReadWord(Cmd.AtRateTimeToEmpty); }
        public ushort ReadAtRateOK() { return ReadWord(Cmd.AtRateOK); }
        public ushort ReadTemperature() { return ReadWord(Cmd.Temperature); }
        public ushort ReadVoltage() { return ReadWord(Cmd.Voltage); }
        public ushort ReadCurrent() { return ReadWord(Cmd.Current); }
        public ushort ReadAverageCurrent() { return ReadWord(Cmd.AverageCurrent); }
        public ushort ReadMaxError() { return ReadWord(Cmd.MaxError); }
        public ushort ReadRelStateOfCharge() { return ReadWord(Cmd.RelStateOfCharge); }
        public ushort ReadAbsStateOfCharge() { return ReadWord(Cmd.AbsStateOfCharge); }
        public ushort ReadRemainingCapacity() { return ReadWord(Cmd.RemainingCapacity); }
        public ushort ReadFullChargeCapacity() { return ReadWord(Cmd.FullChargeCapacity); }
        public ushort ReadRunTimeToEmpty() { return ReadWord(Cmd.RunTimeToEmpty); }
        public ushort ReadAverageTimeToEmpty() { return ReadWord(Cmd.AverageTimeToEmpty); }
        public ushort ReadAverageTimeToFull() { return ReadWord(Cmd.AverageTimeToFull); }
        public ushort ReadChargingCurrent() { return ReadWord(Cmd.ChargingCurrent); }
        public ushort ReadChargingVoltage() { return ReadWord(Cmd.ChargingVoltage); }
        public ushort ReadBatteryStatus() { return ReadWord(Cmd.BatteryStatus); }
        public ushort ReadCycleCount() { return ReadWord(Cmd.CycleCount); }
        public ushort ReadDesignCapacity() { return ReadWord(Cmd.DesignCapacity); }
        public ushort ReadDesignVoltage() { return ReadWord(Cmd.DesignVoltage); }
        public ushort ReadSpecificationInfo() { return ReadWord(Cmd.SpecificationInfo); }
        public ushort ReadManufacturerDate() { return ReadWord(Cmd.ManufacturerDate); }
        public ushort ReadSerialNumber() { return ReadWord(Cmd.SerialNumber); }
        public ushort ReadManufacturerName() { return ReadWord(Cmd.ManufacturerName); }
        public ushort ReadDeviceName() { return ReadWord(Cmd.DeviceName); }
        public ushort ReadDeviceChemistry() { return ReadWord(Cmd.DeviceChemistry); } 
        public ushort ReadManufacturerData() { return ReadWord(Cmd.ManufacturerData); }

        private byte[] _address = new byte[1];
        private byte[] _readWordBuff = new byte[2];
        private byte[] _writeWordBuff = new byte[3];
        private I2CDevice.I2CTransaction[] _wrTransaction = new I2CDevice.I2CTransaction[2];
        private I2CDevice.I2CTransaction[] _wTransaction = new I2CDevice.I2CTransaction[1];

        private ushort ReadWord(Cmd command)
        {
            _address[0] = (byte)command;
            _wrTransaction[0] = CreateWriteTransaction(_address);
            _wrTransaction[1] = CreateReadTransaction(_readWordBuff);
            var try_count = 5;

            while (Execute(_wrTransaction, 500) == 0)
            {
                if (--try_count > 0)
                    continue;

                Debug.Print("SMBUS Read Error");
                return 0; //TODO Log Error

            }

            ushort retval = (ushort) _readWordBuff[0];
            retval += (ushort)(((ushort) _readWordBuff[1]) << 8);
            return retval;
        }

#endregion

        #region Write Functions

        public void WriteRemainingCapAlarm(ushort capAlarmThreshold) { WriteWord(Cmd.RemainingCapAlarm,capAlarmThreshold); }
        public void WriteRemainingTimeAlarm(ushort timeAlarmThreshold) { WriteWord(Cmd.RemainingTimeAlarm, timeAlarmThreshold);}
        public void WriteBatteryMode(ushort flagsBatteryMode) { WriteWord(Cmd.BatteryMode,flagsBatteryMode);}
        public void WriteAtRate(ushort estimatedCurrent) { WriteWord(Cmd.AtRate,estimatedCurrent); }

        private int WriteWord(Cmd command, ushort value)
        {
            _writeWordBuff[0] = (byte) command;
            _writeWordBuff[1] = (byte) (value & 0x00FF); //LSB
            _writeWordBuff[2] = (byte) ((value & 0xFF00) >> 8);//MSB

            var try_count = 1;
            int retval;
            _wTransaction[0] = CreateWriteTransaction(_writeWordBuff);
            while ((retval=Execute(_wTransaction, 500)) == 0) {
                if (--try_count > 0)
                    continue;

                //Debug.Print("SMBUS Read Error");
                return retval; //TODO Log Error

            }

            return retval; //number of bytes written

        }

        #endregion

        #region Compound Functions

        public


        #endregion
        enum Cmd : byte
        {
            MfgAccess = 0x00,
            RemainingCapAlarm, //Remaining Capacity Alarm Threshold mAh
            RemainingTimeAlarm, //Remaining Time Alarm Threshold minutes
            BatteryMode, //Battery Operational Modes (bitflags)
            AtRate, //first half of a 2 function call-set used to set the AtRate value used in calculations makde by the other AT functions (mA)
            AtRateTimeToFull, //returns the pericted remaining time to fully charge at the the atrate() value (minutes)
            AtRateTimeToEmpty, //Returns the predicted remaining operating time at the atrate() value (minutes)
            AtRateOK, //Returns a bool that indicates whether the batt can deliver the atrae()  value of energy for 10 more seconds
            Temperature, //Returns Internal temperature (0.1 degK)
            Voltage, //Returns the battery's voltage (mV)
            Current, //Returns the current moving through the batt's terminals (mA)
            AverageCurrent, //Returns a rolling average based upon the last 64 samples (mA)
            MaxError, //Returns the expected margin of error
            RelStateOfCharge, //Returns the predicted remaining battery capacity in % of Full
            AbsStateOfCharge, //Returns the predicted remaining battery capacity as a % of Design
            RemainingCapacity, //Returns the predicted remaining battery capacity (mAh)
            FullChargeCapacity, //Returns the predicted battery capacity when fully charged (mAh)
            RunTimeToEmpty, //Returns the predicted remaining battery life at the present rate of discharge (minutes)
            AverageTimeToEmpty, //Returns the rolloing average of the predicted remaining battery life (minutes)
            AverageTimeToFull, //Returns the rolling average of the predicted remaining time until the battery reaches full charge (minutes)
            ChargingCurrent, //Returns the battery's desired charging rate (mA)
            ChargingVoltage, //Returns the battery's desired charging voltage (mV)
            BatteryStatus, //Returns the battery's status word (bit flags)
            CycleCount, //Returns the number of cycles the battery has experienced. A cycle is defined as: an amount of discharge approximately equal to the value of the DesignCapacity (cycles)
            DesignCapacity, //Returns the theoretical capacity of the new battery (mA)
            DesignVoltage, //Return the theoretical voltage of a new battery (mV)
            SpecificationInfo, //Returns the version number of SBDS the battery pack supports, as well as voltage and cuyrrrent scaling information. (Formatted Word)
            ManufacturerDate, //Returns the date the electronics was manufactured (Formatted word)
            SerialNumber, //Returns the electronics serial number (number)
            ManufacturerName = 0x20, //Reurns a character array containing the manufacturer's name (string)
            DeviceName, //Returns a character array that contains the battery's name 
            DeviceChemistry, //Returns a character array that contains the battery's chemistry
            ManufacturerData //Returns data specific to the manufacturer
        }

        public void SetModeDefaults()
        {
            var current_mode = ReadBatteryMode();
            current_mode |= 1 << 13;
            current_mode |= 1 << 14;
            current_mode &= 0xFCFF; // ~0x0300

            WriteBatteryMode(current_mode);
        }
    }
}