// Created with MonoDevelop
//
//    SearchIPAddr: find spectrometer in local network
//    Copyright (C) 2020 A. Höpfner, Hiperscan GmbH Dresden, Germany
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using Hiperscan.Common.RegularExpressions;
using Tmds.MDns;

namespace Hiperscan.Common.SearchIpAddress
{
    public partial class SearchIPAdr
    {
        private void SearchServicesByMdns()
        {
            this.IsMdnsSearchFinished = false;

            foreach( string mdns_service in this.SearchParameter.MdnsServiceTypes)
            {
                this.MdnsSearchService(mdns_service, false);
                if (this.FullAddresses.Count > 0)
                    continue;

                if (this.SearchParameter.Restrictions == SearchRestrictions.OnlyFirstDevice)
                    break;

                Thread.Sleep(3000);
                this.MdnsSearchService(mdns_service, true);
            }
        }

        private void MdnsSearchService(string mdns_service, bool second_chance)
        {
            if (second_chance == false)
                Console.WriteLine("Search by MDNS for service = {0} ...", mdns_service);
            else
                Console.WriteLine("Search by MDNS (second chance) for service = {0} ...", mdns_service);

            ServiceBrowser service_browser = new ServiceBrowser();
            service_browser.ServiceAdded += this.OnServiceAdded;

            service_browser.StartBrowse(mdns_service);
            this.WaitForMdnsTimeOut();
            service_browser.StopBrowse();
            service_browser.ServiceAdded -= this.OnServiceAdded;

            GC.Collect();
            System.Threading.Thread.Sleep(1000);
            GC.Collect();
        }

        private void OnServiceAdded(object sender, ServiceAnnouncementEventArgs e)
        {
            lock (this.SynchGate)
            {
                if (this.IsMdnsSearchFinished == true)
                    return;

                if (this.CheckMdnsAnnoucement(e.Announcement) == false)
                    return;

                this.SearchParameter.AddServiceFound(e.Announcement.Type);
                this.FullAddresses.Add(new FullAddress(e.Announcement.Addresses[0].ToString(), e.Announcement.Port));
                this.PrintService('+', e.Announcement);

                this.CheckForEndOfSearch();
            }
        }

        private bool CheckMdnsAnnoucement(ServiceAnnouncement announcement)
        {
            FullAddress full_adddress = new FullAddress(announcement.Addresses[0].ToString(), announcement.Port);
            if (full_adddress.IsValid == false)
                return false;

            if (this.FullAddresses.Contains(full_adddress) == true)
                return false;

            if (this.SearchParameter.Restrictions == SearchRestrictions.FirstDevicePerService)
            {
                if (this.SearchParameter.HasServiceAnAdresses(announcement.Type))
                    return false;
            }

            return true;
        }

        private void WaitForMdnsTimeOut()
        {
            DateTime end = DateTime.Now.AddMilliseconds(this.SearchParameter.TimeoutMSec);
            for (; ; )
            {
                if (this.IsMdnsSearchFinished == true)
                    break;

                if (DateTime.Now > end)
                    break;

                Thread.Sleep(50);
            }
        }

        private void PrintService(char start_char, ServiceAnnouncement service)
        {
            Console.WriteLine("{0} '{1}' on {2}", start_char, service.Instance, service.NetworkInterface.Name);
            Console.WriteLine("\tHost: {0} ({1})", service.Hostname, string.Join(", ", service.Addresses));
            Console.WriteLine("\tPort: {0}", service.Port);
            Console.WriteLine("\tTxt : [{0}]", string.Join(", ", service.Txt));
        }

        private bool IsMdnsSearchFinished { get; set; } = true;
    }
}