using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

// für Webcam
using AForge.Video;
using AForge.Video.DirectShow;

// für Netzwerk
using System.IO;
using System.Net.Sockets;
using System.Net;

using System.Threading;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        #region Webcam

        //Unser Webcam Objekt
        VideoCaptureDevice videoSource;

        void InitWebCam(int nr)
        {
            //Auflistung aller Webcam/Videogeräte
            FilterInfoCollection videosources = new FilterInfoCollection(FilterCategory.VideoInputDevice);

            //Überprüfen, ob mindestens eine Webcam gefunden wurde
            if (videosources != null)
            {
                //Die Webcam "nr" an unser Webcam Objekt binden
                videoSource = new VideoCaptureDevice(videosources[nr].MonikerString);

                try
                {
                    //Überprüfen ob die Webcam Technische-Eigenschaften mitliefert
                    if (videoSource.VideoCapabilities.Length > 0)
                    {
                        string lowestSolution = "10000;0";
                        //Das Profil mit der niedrigsten Auflösung suchen
                        for (int i = 0; i < videoSource.VideoCapabilities.Length; i++)
                        {
                            if (videoSource.VideoCapabilities[i].FrameSize.Width < Convert.ToInt32(lowestSolution.Split(';')[0]))
                                lowestSolution = videoSource.VideoCapabilities[i].FrameSize.Width.ToString() + ";" + i.ToString();
                        }
                        //Dem Webcam Objekt die niedrigstmögliche Auflösung übergeben
                        videoSource.DesiredFrameSize = videoSource.VideoCapabilities[Convert.ToInt32(lowestSolution.Split(';')[1])].FrameSize;
                    }
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.ToString());
                }

                //Dem Webcam Objekt den NewFrame Eventhandler zuweisen.
                //Dieser schlägt bei jedem eingehenden Bild der Webcam an
                videoSource.NewFrame += new AForge.Video.NewFrameEventHandler(videoSource_NewFrame);

                //Die Webcam aktivieren
                videoSource.Start();
            }
        }

        void videoSource_NewFrame(object sender, AForge.Video.NewFrameEventArgs eventArgs)
        {
            //Jedes ankommende Bild von der Webcam der Picturebox zuweisen
            pictureBoxVideoSelf.BackgroundImage = (Image)eventArgs.Frame.Clone();
        }

        #endregion

        Image LastImageSent = null; // zuletzt korrekt gesendetes Bild
        Image LastImageReceived = null; // zuletzt korrekt empfangenes Bild

        Thread Sender; // Thread zum Senden von Bildern
        Thread Receiver; // Thread zum Empfangen von Bildern

        bool Closing = false; // true wenn Formular beendet werden soll
        String ClosingString = "FORM#CLOSING"; // Nachricht, die bei Beendigung geschickt wird
        byte[] ClosingBytes; // Byte - Darstellung der Nachricht
        ASCIIEncoding ByteConverter = new ASCIIEncoding(); // Objekt zur Konvertierung von Strings in byte - Arrays und umgekehrt

        private void button1_Click(object sender, EventArgs e)
        {
            IPAddress[] loaclIPs = Dns.GetHostAddresses(Dns.GetHostName());
            // Thread zum Senden des eigenen Bildes starten
            Sender = new Thread(new ParameterizedThreadStart(this.Send));
            Sender.Start(int.Parse(textBox4.Text));

            // Thread zum Empfangen des Partnerbildes starten
            Receiver = new Thread(new ParameterizedThreadStart(Receive));
            Receiver.Start(/*textBox2.Text*/"4444" + "-" + "169.254.99.198"/*textBox1.Text*/);

            ClosingBytes = ByteConverter.GetBytes(ClosingString);
        }

        private void Send(object port)
        {
            InitWebCam(int.Parse(textBox3.Text)); // Webcam starten Textbox 3=WebCamNummer

            TcpListener Server = new TcpListener(int.Parse(port.ToString()));
            Server.Start();

            TcpClient Client = Server.AcceptTcpClient();

            NetworkStream ClientStream = Client.GetStream();

            while (true)
            {
                if (Closing)
                    break; // Beendigung
                try
                {
                    // probieren, das aktuelle Bild an den Partner zu senden, dieses dann als Backupbild speichern
                    WriteImage((Image)pictureBoxVideoSelf.BackgroundImage.Clone(), ClientStream);
                    LastImageSent = (Image)pictureBoxVideoSelf.BackgroundImage.Clone();
                    Thread.Sleep(100);
                }
                catch
                {   // konnte das aktuelle Bild nicht gesendet werden, das Backupbild senden
                    WriteImage(LastImageSent, ClientStream);
                }
            }

            try
            {
                ClientStream.Write(ClosingBytes, 0, ClosingBytes.Length);
            }
            catch { };
        }
        private void WriteImage(Image image, NetworkStream stream)
        {
            ASCIIEncoding Encoder = new ASCIIEncoding();
            MemoryStream TempStream = new MemoryStream();
            byte[] Buffer;

            try
            {
                // das übergebene Bild in den aktuellen Stream schreiben
                image.Save(TempStream, System.Drawing.Imaging.ImageFormat.Gif);
            }
            catch
            {
            }

            Buffer = TempStream.ToArray();

            // die Größe des Bildes als 20-stelligen String darstellen, mit "x" auffüllen
            string ImageSize = Buffer.Length.ToString();
            while (ImageSize.Length < 20)
                ImageSize += "x";

            // die Größe plus die Daten des Bildes in ein Array schreiben
            byte[] FittedImageSize = Encoder.GetBytes(ImageSize);
            byte[] ImagePlusSize = new byte[FittedImageSize.Length + Buffer.Length];
            Array.Copy(FittedImageSize, ImagePlusSize, FittedImageSize.Length);
            Array.Copy(Buffer, 0, ImagePlusSize, FittedImageSize.Length, Buffer.Length);

            try
            {
                // das zusammengefasste Array schreiben
                stream.Write(ImagePlusSize, 0, ImagePlusSize.Length);
                stream.Flush();
            }
            catch
            {
                // kann der Stream nicht mehr beschrieben werden, hat der Partner beendet
                Closing = true;
            }
        }

        private void Receive(object portip)
        {
            // portip hat die Form "port-ip"
            string[] Parameter = portip.ToString().Split('-');
            System.Net.IPAddress IP = System.Net.IPAddress.Parse(Parameter[1]);

            TcpClient Exchange = new TcpClient();
            NetworkStream ExchangeStream = null;

            Image TempImage;

            // alle 3 Sekunden versuchen eine Verbindung herzustellen
            while (true)
            {
                try
                {
                    Exchange.Connect(IP, int.Parse(Parameter[0]));
                    ExchangeStream = Exchange.GetStream();
                    break;
                }
                catch
                {
                    Thread.Sleep(3000);
                }
            }

            while (true)
            {
                if (Closing)
                    break; // Beendigung

                try
                {
                    // versuchen, das empfangene Bild zu lesen darzustellen
                    // bei Erfolg
                    TempImage = ReadImage(ExchangeStream);
                    if (TempImage == null)
                        throw new Exception();

                    pictureBoxVideoPartner.BackgroundImage = TempImage;
                    LastImageReceived = (Image)pictureBoxVideoPartner.BackgroundImage.Clone();
                    Thread.Sleep(100);
                }
                catch
                {
                    try
                    {   // bei Fehler das Backupbild darstellen
                        pictureBoxVideoPartner.BackgroundImage = LastImageReceived;
                    }
                    catch { }
                }
            }
        }
        private Image ReadImage(NetworkStream stream)
        {
            Image Result;
            int BytesRead;

            // die ersten 20 Bytes des Streams einlesen, denn in diesen ist die Größe kodiert
            byte[] ImageSize = new byte[20];
            BytesRead = stream.Read(ImageSize, 0, 20);

            /* konnten nur 12 Bytes gelesen werden und haben diese den Inhalt des
            Closing Strings, so soll beendet werden */
            if (BytesRead == 12)
            {
                if (ByteConverter.GetString(ImageSize, 0, 12) == "FORM#CLOSING")
                {
                    Closing = true;
                    return null;
                }
            }

            byte[] ErrorBuffer = new byte[100000000];

            ASCIIEncoding Decoder = new ASCIIEncoding();
            string ImageSizeString = Decoder.GetString(ImageSize).Replace("x", "");

            int TestSize;

            if (!int.TryParse(ImageSizeString, out TestSize))
            {
                stream.Read(ErrorBuffer, 0, ErrorBuffer.Length);
                return null;
            }

            byte[] ImageFile = new byte[int.Parse(ImageSizeString)];

            stream.Read(ImageFile, 0, ImageFile.Length);

            MemoryStream temps = new MemoryStream();

            try
            {
                temps.Write(ImageFile, 0, ImageFile.Length);
                Result = Image.FromStream(temps);
                return Result;
            }
            catch
            {
                return null;
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            //Webcam beim Schließen des Programms wieder freigeben
            if (videoSource != null && videoSource.IsRunning)
            {
                videoSource.SignalToStop();
                videoSource = null;
            }

            Closing = true;

            Thread.Sleep(3000);

            if (Sender != null && Sender.IsAlive)
                Sender.Abort();

            if (Receiver != null && Receiver.IsAlive)
                Receiver.Abort();
        }
    }
}
