using System;
using System.Collections.Generic;
using System.Text;
using everdrive3x.Hardware;
using System.Threading;
using System.IO;
using everdrive3x.Rom;
namespace everdrive3x
{
    class Dump
    {
        Form1 main_form;
        Thread dump_thread;
        Thread state_cobtrol_thread;
        bool thread_state;
        Device device;
        FileStream file;
        String file_name_arg;
        String dump_size_arg;
        int bytes_recive;
        int dump_size;
        RomInterface rom_header;


        public Dump(Form1 main)
        {
            main_form = main;
        }

        public void start(String file, String size)
        {
            bytes_recive = 0;
            file_name_arg = file;
            dump_size_arg = size;
            thread_state = true;
            dump_thread = new Thread(initDump);
            dump_thread.Start();
            state_cobtrol_thread = new Thread(stateUpdate);
            state_cobtrol_thread.Start();
        }

        public void stop()
        {
            if (!thread_state) return;
            try
            {   
                thread_state = false;
                device.close();
                device = null;
                file.Close();
            }
            catch (Exception) { }

        }

        void initDump()
        {
            try
            {
                
                main_form.lockInterface(true);
                main_form.addToConsole("---------------------------------------------------");
                main_form.addToConsole("search device...");
                device = Device.searchDevice();
                main_form.addToConsole("OK: device detected");
                main_form.addToConsole("DEVICE name: " + device.Profile.Name);
                rom_header = RomHeader.getHeader(device);
                dump_size = getDumpSize(device);
                main_form.addToConsole("DUMP platform: " + rom_header.Platform);
                main_form.addToConsole("DUMP name: " + rom_header.Name);
                main_form.addToConsole("DUMP size: " + dump_size / 1024 + "kb");             
                device.Control.resetByPass();
                device.Control.setAddr(0);
                if (!file_name_arg.Contains("." + Rom.RomTools.getRomExtension(device))) file_name_arg += "." + Rom.RomTools.getRomExtension(device);
                file = File.Create(file_name_arg);
                main_form.addToConsole("start dumping...");
                dumping();
                main_form.addToConsole("OK: dumping done");
            }
            catch (Exception x)
            {
                if (thread_state)
                {
                    main_form.addToConsole("ERROR: " + x.Message);
                }
            }
            if (thread_state)
            {
                Thread.Sleep(600);
                stop();
                main_form.lockInterface(false);
            }
            
        }

        void dumping()
        {
            int block_size;
            bytes_recive = 0;
            byte[] buff;

            while (thread_state && bytes_recive < dump_size)
            {
                try
                {
                    block_size = Math.Min(Communication.BUFF_SIZE, dump_size - bytes_recive);
                    buff = device.Control.readBlock(block_size);
                    file.Write(buff, 0, block_size);
                    bytes_recive += block_size;
                }
                catch (TimeoutException)
                {
                    main_form.addToConsole("WARNING: timeout");
                }

            }
        }

        int getDumpSize(Device dev)
        {
            
            int dump_size = 0;


            if (dump_size_arg.EndsWith("max") && dev.Profile.MemorySize > 0)
            {
                return dev.Profile.MemorySize;
            }

            if (dump_size_arg.EndsWith("auto"))
            {
                dump_size = rom_header.DeclaratedSize;
                if (dump_size > 0 && dump_size <= device.Profile.MemorySize) return dump_size;
            }

            if (dump_size_arg.EndsWith("256k")) return 262144;
            if (dump_size_arg.EndsWith("512k")) return 524288;
            if (dump_size_arg.EndsWith("1m")) return 1048576;
            if (dump_size_arg.EndsWith("2m")) return 1048576 * 2;
            if (dump_size_arg.EndsWith("4m")) return 1048576 * 4;
            if (dump_size_arg.EndsWith("8m")) return 1048576 * 8;
            if (dump_size_arg.EndsWith("16m")) return 1048576 * 16;

            return 1048576 * 4;
        }

        void stateUpdate()
        {
            DateTime start_time = System.DateTime.Now;
            String speed = "";
            String time = "";
            String traff = "";
            long speed_time;
            int progress = 0;

            while (thread_state)
            {
                try
                {
                    progress = bytes_recive * 100 / (dump_size + 1);
                    speed_time = (System.DateTime.Now - start_time).Ticks / 100000;
                    if (speed_time < 1) speed_time = 1;
                    speed = "" + bytes_recive / speed_time * 100 / 1024;
                    time = "" + (System.DateTime.Now - start_time).Minutes + ":" + (System.DateTime.Now - start_time).Seconds;
                    traff = "" + bytes_recive / 1024 + "kb";
                    if (thread_state) if (thread_state) main_form.stateUpdate(progress, speed, time, traff);
                    if (thread_state) Thread.Sleep(500);
                }
                catch (Exception) { }
            }
        }
    }
}
