using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using Newtonsoft.Json;
using ScottPlot.Drawing;
using ScottPlot;
using ScottPlot.Drawing.Colormaps;
using System.Globalization;

namespace Pratica2
{
    
    public partial class FormTelaPrincipal : Form
    {
        //MODULO 1:
        Thread threadRecebimentosPacotesModulo1 = null;
        Thread[] listaThreadsUnidadesGeradorasModulo1;
        UnidadeGeradoraDadosMedicao[] listaUnidadesGeradorasDadosMedicao;
        UdpClient usocketConexaoUDPModulo1 = null;
        //IAsyncResult resultado;
        IPEndPoint ipConexaoRecebimentoUDPModulo1 = null;
        IPEndPoint ipConexaoEnvioUDPModulo1 = null;
        string menssagemRecebidaModulo1;
        byte[] bytesRecebidosModulo1;
        private List<int> dadosPlotarGraficoModulo1 = new List<int>();
        JSON_DADOS_RECEBIDOS_CORRENTE dadosRecebidosEmFormatoJSONModulo1;
        int contadorRecebimentoPacoteModulo1 = 0;
        public Mutex nossoMutex = new Mutex();
        bool pararRecebimentoDadosModulo1 = false;

        //MODULO 2:
        Thread threadRecebimentosPacotesModulo2 = null;
        Thread threadEnvioPacotesModulo2 = null;
        UdpClient usocketConexaoUDPModulo2 = null;
        IPEndPoint ipConexaoRecebimentoUDPModulo2 = null;
        IPEndPoint ipConexaoEnvioUDPModulo2 = null;
        string menssagemRecebidaModulo2;
        string formatoPacoteModulo2;
        byte[] bytesRecebidosModulo2;
        byte[] bytesAEnviarModulo2;
        private List<int> dadosPlotarGraficoModulo2 = new List<int>();
        int contadorRecebimentoPacoteModulo2 = 0;
        bool pararRecebimentoDadosModulo2 = false;
        bool pararEnvioDadosModulo2 = false;
        double latitude ;
        double longitude ;


        public FormTelaPrincipal()
        {
            InitializeComponent();
            Control.CheckForIllegalCrossThreadCalls = false; //possibilita que componentes sejam chamados por threads diferentes                                    //PASSO 1: encontra o IP e atribui a classe principal de dados e controle 
            formsPlotPacotesEnviados.Plot.Title("Taxa pacotes recebidos de subestação", true, Color.Black, 12.0f);
            formsPlotDadosRecebidosModulo2.Plot.Title("Taxa pacotes recebidos de telemetria", true, Color.Black, 12.0f);
        }

        private void listViewMod1UnidGeradora_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (listaUnidadesGeradorasDadosMedicao == null)
                return;


            int indiceUnidGeradora = 0;
            if (listViewMod1UnidGeradora.SelectedItems.Count > 0)
                indiceUnidGeradora = Convert.ToInt16(listViewMod1UnidGeradora.SelectedItems[0].Text);
            else 
                return;


            for(int i = 0; i < listaUnidadesGeradorasDadosMedicao.Length; i++)
            {
                if(listaUnidadesGeradorasDadosMedicao[i].codDestaUnidade == indiceUnidGeradora)
                {
                    propertyGridUnidGeradoras.SelectedObject = listaUnidadesGeradorasDadosMedicao[i];
                    return;
                }
            }
        }

        private void buttonIniciarParte1SV_Click(object sender2, EventArgs e)
        {
            //parte 0: inicializacoes de interface
            buttonIniciarMod1Iniciar.Enabled = false;   
            propertyGridUnidGeradoras.Enabled = true;
            buttonPararEnvioV1.Enabled = true;
            timerPlotaModulo1SinaisEnviados.Start(); //para plotar sinais
            contadorRecebimentoPacoteModulo1 = 0;
            dadosPlotarGraficoModulo1.Clear();
            pararRecebimentoDadosModulo1 = false;


            //Parte 1: inicia conexao UDP
            usocketConexaoUDPModulo1 = new UdpClient(12345);
            ipConexaoRecebimentoUDPModulo1 = new IPEndPoint(IPAddress.Any, 12345);
            ipConexaoEnvioUDPModulo1 = new IPEndPoint(IPAddress.Parse("255.255.255.255"), 12345);
            //textBoxRecepcaoConexao.Text = textBoxRecepcaoConexao.Text + "UDP CONECTADO !!!" + Environment.NewLine;
            threadRecebimentosPacotesModulo1 = new Thread(RecebimentoPacotesUDPModulo1);
            threadRecebimentosPacotesModulo1.Start();


            //Parte 2: gera objetos equivalantes a unidades geradoras
            int quantidadeUnidGeradoras = listViewMod1UnidGeradora.Items.Count;
            listaUnidadesGeradorasDadosMedicao = new UnidadeGeradoraDadosMedicao[quantidadeUnidGeradoras];
            listaThreadsUnidadesGeradorasModulo1 = new Thread[quantidadeUnidGeradoras];

            int codigo = 0;
            int freqEnvio = 0;
            int correnteOriginal = 0;
            int correnteCurto = 0;
            for (int i = 0; i < quantidadeUnidGeradoras; i++)
            {
                codigo = Convert.ToInt16(listViewMod1UnidGeradora.Items[i].SubItems[0].Text);
                freqEnvio = Convert.ToInt16(listViewMod1UnidGeradora.Items[i].SubItems[1].Text);
                correnteOriginal = Convert.ToInt16(listViewMod1UnidGeradora.Items[i].SubItems[2].Text);
                correnteCurto = Convert.ToInt16(listViewMod1UnidGeradora.Items[i].SubItems[3].Text);
                listaUnidadesGeradorasDadosMedicao[i] = new UnidadeGeradoraDadosMedicao(usocketConexaoUDPModulo1, ipConexaoEnvioUDPModulo1, nossoMutex, correnteOriginal, codigo, freqEnvio, correnteCurto, false);
            }
            

            //Parte 3: gera threads
            for (int i = 0; i < quantidadeUnidGeradoras; i++)
            {
                listaThreadsUnidadesGeradorasModulo1[i] = new Thread(new ThreadStart(listaUnidadesGeradorasDadosMedicao[i].EnviaPacotesUDPFrequentemente));
                listaThreadsUnidadesGeradorasModulo1[i].Name = "UnidGeradora" + i.ToString();
                listaThreadsUnidadesGeradorasModulo1[i].Start();
            }
           
        }

        private void buttonPararEnvioV1_Click(object sender, EventArgs e)
        {
            //parte 0: inicializacoes de interface
            propertyGridUnidGeradoras.Enabled = false;
            buttonPararEnvioV1.Enabled = false;
            timerPlotaModulo1SinaisEnviados.Stop(); //para plotar sinais
            contadorRecebimentoPacoteModulo1 = 0;
            dadosPlotarGraficoModulo1.Clear();
            buttonIniciarMod1Iniciar.Enabled = true;
            pararRecebimentoDadosModulo1 = true;

            //Parte 1: para os objetos equivalantes a unidades geradoras            
            for (int i = 0; i < listaUnidadesGeradorasDadosMedicao.Length; i++)
            {
                listaUnidadesGeradorasDadosMedicao[i].pararEnvio = true; //vai forcar as threads pararem
            }
            Thread.Sleep(500);
            listaUnidadesGeradorasDadosMedicao = null;

            //Parte 2: fecha conexao UDP
            usocketConexaoUDPModulo1.Close();
            threadRecebimentosPacotesModulo1.Abort();
        }

        private void RecebimentoPacotesUDPModulo1()
        {
            while (true)
            {
                if (pararRecebimentoDadosModulo1)//para 
                    return;
                bytesRecebidosModulo1 = usocketConexaoUDPModulo1.Receive(ref ipConexaoRecebimentoUDPModulo1);
                //menssagemRecebidaModulo1 = Encoding.ASCII.GetString(bytesRecebidosModulo1);
                //dadosRecebidosEmFormatoJSONModulo1 = JsonConvert.DeserializeObject<JSON_DADOS_RECEBIDOS_CORRENTE>(menssagemRecebida);
                //textBoxRecepcaoConexao.Text = textBoxRecepcaoConexao.Text + "RECEBEU_UDP (" + ipConexaoRecebimentoUDP.Address.ToString() + ") >>" + menssagemRecebida + Environment.NewLine;
                contadorRecebimentoPacoteModulo1++;
            }
        }

        private void buttonMod1Limpar_Click(object sender, EventArgs e)
        {
            listViewMod1UnidGeradora.Items.Clear();
        }

        private void buttonAdicionar_Click(object sender, EventArgs e)
        {
            listViewMod1UnidGeradora.Items.Add(new ListViewItem(new String[] { numericUpDownMod1CodUnidGen.Value.ToString(), numericUpDownMod1FreqEnvio.Value.ToString(), numericUpDownMod1Corrente.Value.ToString(), numericUpDownMod1Curto.Value.ToString() }));
            numericUpDownMod1CodUnidGen.Value = numericUpDownMod1CodUnidGen.Value + 1; 
        }

        private void timerPlotaModulo1SinaisEnviados_Tick(object sender, EventArgs e)
        {
            if (dadosPlotarGraficoModulo1.Count > 300) //se tiver muitas amostras, zera
            {
                dadosPlotarGraficoModulo1.Clear();
            }
            else
            {
                dadosPlotarGraficoModulo1.Add(contadorRecebimentoPacoteModulo1);
            }
            contadorRecebimentoPacoteModulo1 = 0; //zera contagem


            //ATUALIZA VISUALIZACAO DE GRAFICO:
            double[] ys = new double[dadosPlotarGraficoModulo1.Count];
            double[] xs = DataGen.Consecutive(dadosPlotarGraficoModulo1.Count);
            for (int i = 0; i < dadosPlotarGraficoModulo1.Count; i++)
            {
                ys[i] = (double)dadosPlotarGraficoModulo1[i];
            }
            formsPlotPacotesEnviados.Plot.Clear();
            if (dadosPlotarGraficoModulo1.Count > 1)
            {
                formsPlotPacotesEnviados.Plot.AddScatterLines(xs, ys, Color.Blue, 2);
                formsPlotPacotesEnviados.Refresh();
            }
        }
















        private void buttonIniciarMod2_Click(object sender, EventArgs e)
        {
            //parte 0: inicializacoes de interface
            buttonIniciarMod2.Enabled = false;
            buttonPararMod2.Enabled = true;
            contadorRecebimentoPacoteModulo2 = 0;
            dadosPlotarGraficoModulo2.Clear();
            pararRecebimentoDadosModulo2 = false;
            pararEnvioDadosModulo2 = false;
            progressBarEnvioModulo2.Maximum = (int)numericUpDownQtdePacotes.Value;
            progressBarEnvioModulo2.Value = 0;
            latitude = Convert.ToDouble(textBoxLat.Text);
            longitude = Convert.ToDouble(textBoxLong.Text);


            //Parte 1: inicia conexao UDP
            usocketConexaoUDPModulo2 = new UdpClient(1234);
            ipConexaoRecebimentoUDPModulo2 = new IPEndPoint(IPAddress.Any, 1234);
            ipConexaoEnvioUDPModulo2 = new IPEndPoint(IPAddress.Parse("255.255.255.255"), 1234);
            threadRecebimentosPacotesModulo2 = new Thread(RecebimentoPacotesUDPModulo2);
            threadRecebimentosPacotesModulo2.Start();

            threadEnvioPacotesModulo2 = new Thread(EnvioPacotesUDP_CEPModulo2);
            threadEnvioPacotesModulo2.Start();
            timerPlotaModulo2SinaisEnviados.Start(); //para plotar sinais
        }
        private void buttonPararMod2_Click(object sender, EventArgs e)
        {
            //parte 0: inicializacoes de interface
            buttonIniciarMod2.Enabled = true;
            buttonPararMod2.Enabled = false;
            pararRecebimentoDadosModulo2 = true;
            pararEnvioDadosModulo2 = true;
            timerPlotaModulo2SinaisEnviados.Stop(); //para plotar sinais

            //Parte 1: inicia conexao UDP
            Thread.Sleep(100); // vamos dar um tempo antes de fechar
            usocketConexaoUDPModulo2.Close();
        }

        private void RecebimentoPacotesUDPModulo2()
        {
            while (true)
            {
                if (pararRecebimentoDadosModulo2)
                    return;
                bytesRecebidosModulo2 = usocketConexaoUDPModulo2.Receive(ref ipConexaoRecebimentoUDPModulo2);
                contadorRecebimentoPacoteModulo2++;
            }
        }

        private void EnvioPacotesUDP_CEPModulo2()
        {
           Random rnd = new Random();
           double valorASomar = 0;

           for (int i = 0; i < numericUpDownQtdePacotes.Value; i++)
           {
               if(i < numericUpDownQtdePacotesMesmaArea.Value) //os primeiros pacotes, vou mandar eles pertecendo a uma mesma regiao geografica inserindo pouca variacao nas coordenadas geograficas
                    valorASomar = (double)(rnd.Next(-10, 10) / 5000); //pequena variacao de posicao
               else
                    valorASomar = (double)(rnd.Next(-10, 10) / 50); //grande variacao de posicao
               latitude = latitude + valorASomar;
               longitude = longitude + valorASomar;

                
               formatoPacoteModulo2 = "{'Lat':" + latitude.ToString(CultureInfo.InvariantCulture) +
                                      ",'Long':" + longitude.ToString(CultureInfo.CreateSpecificCulture("en-GB")) +
                                      ",'codErro':95}";
                //"N", CultureInfo.CreateSpecificCulture("en-US")
                progressBarEnvioModulo2.Value = i;
                bytesAEnviarModulo2 = Encoding.ASCII.GetBytes(formatoPacoteModulo2);
                if (usocketConexaoUDPModulo2 != null)
                    usocketConexaoUDPModulo2.Send(bytesAEnviarModulo2, bytesAEnviarModulo2.Length, ipConexaoEnvioUDPModulo2);
                if (pararEnvioDadosModulo2)
                    return;
            }
            timerPlotaModulo2SinaisEnviados.Stop(); //para plotar sinais
        }

        private void timerPlotaModulo2SinaisEnviados_Tick(object sender, EventArgs e)
        {
            if (dadosPlotarGraficoModulo2.Count > 300) //se tiver muitas amostras, zera
            {
                dadosPlotarGraficoModulo2.Clear();
            }
            else
            {
                dadosPlotarGraficoModulo2.Add(contadorRecebimentoPacoteModulo2);
            }
            contadorRecebimentoPacoteModulo2 = 0; //zera contagem

            double[] ys = new double[dadosPlotarGraficoModulo2.Count];
            double[] xs = DataGen.Consecutive(dadosPlotarGraficoModulo2.Count);
            for (int i = 0; i < dadosPlotarGraficoModulo2.Count; i++)
            {
                ys[i] = (double)dadosPlotarGraficoModulo2[i];
            }
            formsPlotDadosRecebidosModulo2.Plot.Clear();
            if (dadosPlotarGraficoModulo2.Count > 1)
            {
                formsPlotDadosRecebidosModulo2.Plot.AddScatterLines(xs, ys, Color.Blue, 2);
                formsPlotDadosRecebidosModulo2.Refresh();
            }
        }
    }//fim classe









    public class JSON_DADOS_RECEBIDOS_CORRENTE
    {
        public int Ia { get; set; }
        public int Ib { get; set; }
        public int Ic { get; set; }
        public int numPacote { get; set; }
        public int idDispositivo { get; set; }
    }











    public class UnidadeGeradoraDadosMedicao
    {
        [Browsable(true)]         [ReadOnly(false)]         [Description("Valor de corrente emitido pelo pacote")]     [DisplayName("Corrente normal")]
        public int valorCorrente  { get; set; }

        [Browsable(true)]
        [ReadOnly(false)]
        [Description("Código desta unidade geradora de pacotes")]
        [DisplayName("COD")]
        public int codDestaUnidade { get; set; }

        [Browsable(true)]
        [ReadOnly(false)]
        [Description("Frequencia de envio de pacotes em ms")]
        [DisplayName("Freq. envio")]
        public int freqEnvioPacotesMS { get; set; }

        [Browsable(true)]
        [ReadOnly(true)]
        [Description("Quantidade de pacotes enviados")]
        [DisplayName("Pacotes enviados")]
        public int contadoPacotesEnviados { get; set; }

        [Browsable(true)]
        [ReadOnly(false)]
        [Description("Valor que indica se o item esta em curto")]
        [DisplayName("Valor curto")]
        public int valorCurto { get; set; }

        [Browsable(true)]
        [ReadOnly(false)]
        [Description("Indica se está em curto")]
        [DisplayName("Em curto?")]
        public bool estaCurto { get; set; }

        [Browsable(false)]
        public bool pararEnvio { get; set; }

        public Mutex nossoMutex;
        public UdpClient usocketConexaoUDP;
        public IPEndPoint ipConexaoEnvioUDP;

        public UnidadeGeradoraDadosMedicao(UdpClient p_usocketConexaoUDP, IPEndPoint p_ipConexaoEnvioUDP, Mutex p_nossoMutex, int p_valorCorrente, int p_codDestaUnidade, int p_freqEnvioPacotesMS, int p_valorCurto, bool p_estaCurto)
        {
            nossoMutex = p_nossoMutex;
            valorCorrente = p_valorCorrente;
            codDestaUnidade = p_codDestaUnidade;
            freqEnvioPacotesMS = p_freqEnvioPacotesMS;
            valorCurto = p_valorCurto;
            estaCurto = p_estaCurto;
            usocketConexaoUDP = p_usocketConexaoUDP;
            ipConexaoEnvioUDP = p_ipConexaoEnvioUDP;
            contadoPacotesEnviados = 0;
            pararEnvio = false;
             
        }
        public void EnviaPacotesUDPFrequentemente() //avi ser usaco como uma  thread
        {
            string formatoPacote;
            string corrente;
            byte[] bytes;

            while (true)
            {
                corrente = Convert.ToString(valorCorrente);
                if (contadoPacotesEnviados < 65000)
                    contadoPacotesEnviados++;
                else
                    contadoPacotesEnviados = 0;
                formatoPacote = "{'Ia':" + corrente +
                                ",'Ib':" + corrente +
                                ",'Ic':" + corrente +
                                ",'numPacote':" + contadoPacotesEnviados.ToString() +
                                ",'idDispositivo':" + codDestaUnidade.ToString() + "}";

                bytes = Encoding.ASCII.GetBytes(formatoPacote);
                nossoMutex.WaitOne(); //bloqueia esta regiao para 1 simples thread acessar
                if(usocketConexaoUDP != null)
                    usocketConexaoUDP.Send(bytes, bytes.Length, ipConexaoEnvioUDP);
                nossoMutex.ReleaseMutex(); //desbloqueia esta regiao 
                if (pararEnvio)
                    return;
                if((valorCorrente < valorCurto) || !estaCurto) //se nao estiver em curto
                    Thread.Sleep(freqEnvioPacotesMS); //aguarda ate proximo envio
                else//esta em curto
                    Thread.Sleep(1); //coloquei para nao sobrecarregar
            }

        }

    }
}
