using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace MiniBar2k.Common
{
    internal class fbcMap
    {
        public int start = 0;
        public int end = 0;
    }

    public class FoobarManager
    {
        private FoobarController Controller = null;

        private List<fbcBaseEntry> listCache = new List<fbcBaseEntry>();
        private List<fbcQueueEntry> listQueue = new List<fbcQueueEntry>();
        private List<fbcPlaylist> listPlaylist = new List<fbcPlaylist>();
        private float vol = 0;
        private fbcOrderEnum order = fbcOrderEnum.def;

        private fbcBaseEntry[] searchResult = null;
        private fbcBaseEntry[] listResult = null;
        private bool queueResult = false;
        private bool playlistResult = false;
        private bool volResult = false;
        private bool orderResult = false;
        private bool trackinfoResult = false;

        private int requestStart = 0;

        public bool BlockUI = true;
        public fbcPlayEntry CurrentTrack = null;
        public fbcPlayState CurrentPlayState = fbcPlayState.stop;
        public int CurrentPlaylist = 0;

        //------------- CONSTRUCTORS, DESTRUCTORS, IDISPOSABLE
        
        public FoobarManager(FoobarController controller, int maxsize)
        {
            Controller = controller;
            listCache.Capacity = maxsize;
            ReduceList();
        }

        //------------- UTILITY METHODS

        private void ReduceList()
        {
            if (listCache.Count > listCache.Capacity / 2)
            {
                listCache.RemoveRange(0, listCache.Capacity / 2);
            }

            GC.Collect();
        }

        private bool DoSomeWaiting()
        {
            if (!BlockUI)
            {
                Application.DoEvents();
            }
            Controller.Tick();
            Thread.Sleep(0);

            int cur = System.Environment.TickCount;

            bool res = ((cur < requestStart) || (cur > requestStart + fbcDefaults.TimeOut) || (!Controller.isConnected()));

            if (res)
            {
                Controller.Disconnect();
            }

            return !res;
        }

        private fbcBaseEntry listCacheFind(int playlist, int track)
        {
            fbcBaseEntry entry = null;

            foreach (fbcBaseEntry find in listCache)
            {
                if ((find.PlaylistIndex == playlist) && (find.TrackIndex == track))
                {
                    entry = find;
                    break;
                }
            }

            return entry;
        }

        private void listCacheAdd(fbcBaseEntry entry)
        {
            fbcBaseEntry found = listCacheFind(entry.PlaylistIndex, entry.TrackIndex);

            if (found == null) 
            {
                listCache.Add(entry);

                if (listCache.Count == listCache.Capacity)
                {
                    ReduceList();
                }
            }
        }

        //------------- HANDLERS

        internal void ControllerOnError(object sender)
        {
            searchResult = new fbcBaseEntry[0];
            listResult = new fbcBaseEntry[0];
            queueResult = true;
            playlistResult = true;
            volResult = true;
            orderResult = true;
            trackinfoResult = true;
        }

        internal void ControllerOnSync(object sender, bool isafter)
        {
            if (!isafter)
            {
                listCache.Clear();
                listQueue.Clear();
                listPlaylist.Clear();
                CurrentTrack = null;
                CurrentPlayState = fbcPlayState.stop;
                CurrentPlaylist = 0;
            }
            else
            {
                if (Ready())
                {
                    PlaylistsRefresh();
                    TrackInfoRefresh();
                    Queue(true);
                    Vol(true);
                    Order(true);
                }
            }
        }

        internal void ControllerOnPlay(object sender, fbcPlayState playstate, fbcPlayEntry entry)
        {
            if (sender != null)
            {
                listCacheAdd(entry);

                CurrentPlayState = playstate;
                CurrentTrack = entry;
                CurrentPlaylist = entry.PlaylistIndex;

                trackinfoResult = true;
            }
        }

        internal void ControllerOnVolume(object sender, float volume) 
        {
            vol = volume;
            volResult = true;
        }

        internal void ControllerOnOrder(object sender, fbcOrderEnum order) 
        {
            this.order = order;
            orderResult = true;
        }

        internal void ControllerOnPlaylist(object sender, fbcPlaylist[] playlists, int current)
        {
            listPlaylist.Clear();

            foreach (fbcPlaylist playlist in playlists)
            {
                listPlaylist.Add(playlist);
            }

            CurrentPlaylist = current;
            playlistResult = true;
        }

        internal void ControllerOnSearch(object sender, fbcBaseEntry[] entries)
        {
            searchResult = entries;

            foreach (fbcBaseEntry entry in entries)
            {
                listCacheAdd(entry);
            }
        }

        internal void ControllerOnList(object sender, fbcBaseEntry[] entries)
        {
            listResult = entries;

            foreach (fbcBaseEntry entry in entries)
            {
                listCacheAdd(entry);
            }
        }

        internal void ControllerOnQueue(object sender, fbcQueueEntry[] entries)
        {
            listQueue.Clear();

            foreach (fbcQueueEntry entry in entries)
            {
                listCacheAdd(entry);
                listQueue.Add(entry);
            }

            queueResult = true;
        }

        //------------- PUBLIC METHODS

        public void ClearCachedTitles()
        {
            foreach (fbcBaseEntry entry in listCache)
            {
                entry.DisplayTitleType = fbcTitleType.none;
            }
        }

        public bool Ready()
        {
            bool result = false;

            requestStart = System.Environment.TickCount;

            if (Controller != null)
            {
                result = Controller.isConnected();
            }

            return result;
        }

        public fbcPlaylist Playlist(int playlist) 
        {
            fbcPlaylist result = null;

            if (playlist < listPlaylist.Count)
            {
                result = listPlaylist[playlist];
            }

            return result;
        }

        public fbcPlaylist[] Playlists() 
        {
            return listPlaylist.ToArray();
        }

        public void PlaylistsRefresh()
        {
            if (Ready())
            {
                playlistResult = false;
                Controller.ListInfo();
                while (!playlistResult) { if (!DoSomeWaiting()) { break; } }
            }
        }

        public fbcBaseEntry[] Search(int playlist, String data)
        {
            if (Ready())
            {
                searchResult = null;
                Controller.Search(playlist, data);
                while (searchResult == null) { if (!DoSomeWaiting()) { break; } }
                return searchResult;
            }
            else
            {
                return null;
            }
        }

        public fbcBaseEntry Entry(int playlist, int track)
        {
            fbcBaseEntry result = null;

            if (Ready())
            {
                result = listCacheFind(playlist, track);

                if (result == null)
                {
                    listResult = null;
                    Controller.List(playlist, track, track);
                    while (listResult == null) { if (!DoSomeWaiting()) { break; } }
                    if (listResult.Length > 0) { 
                        result = listResult[0];
                    }
                }
            }

            return result;
        }

        public fbcBaseEntry[] Entries(int playlist, int fromtrack, int totrack)
        {
            fbcBaseEntry[] result = new fbcBaseEntry[totrack - fromtrack + 1];

            for (int i = fromtrack; i <= totrack; i++)
            {
                result[i - fromtrack] = Entry(playlist, i);
            }

            return result;
        }

        public void PrecacheEntries(int playlist, int track)
        {
            int fromtrack = 0;
            int totrack = 0;
            int missingbefore = 0;
            int missingafter = 0;

            fromtrack = track - fbcDefaults.PrecacheSize;
            totrack = track + fbcDefaults.PrecacheSize;

            if (fromtrack < 1) { fromtrack = 1; }
            if (totrack > listPlaylist[playlist].Entries) { totrack = listPlaylist[playlist].Entries; }

            bool[] map = new bool[totrack - fromtrack + 1];
            bool cur = false;

            for (int i = fromtrack; i <= totrack; i++)
            {
                cur = (listCacheFind(playlist, i) == null);
                map[i - fromtrack] = cur;
                if ((cur) && (i <= track)) { missingbefore++; }
                if ((cur) && (i >= track)) { missingafter++; }
            }

            if ((missingbefore >= fbcDefaults.PrecacheCutoff) || (missingafter >= fbcDefaults.PrecacheCutoff))
            {
                List<fbcMap> retrieveMap = new List<fbcMap>();

                fbcMap current = null;
                for (int i = fromtrack; i <= totrack; i++)
                {
                    if (!map[i - fromtrack])
                    {
                        if (current != null)
                        {
                            if (current.start != 0)
                            {
                                current.end = i - 1;
                                retrieveMap.Add(current);
                                current = null;
                            }
                        }
                    }
                    else
                    {
                        if (current == null)
                        {
                            current = new fbcMap();
                            current.start = i;
                        }
                    }
                }
                if (current != null)
                {
                    current.end = totrack;
                    retrieveMap.Add(current);
                }

                foreach (fbcMap getMap in retrieveMap)
                {
                    listResult = null;
                    Controller.List(playlist, getMap.start, getMap.end);
                    while (listResult == null) { if (!DoSomeWaiting()) { break; } }
                }
            }
        }

        public void EntriesRefresh()
        {
            listCache.Clear();
        }

        public fbcQueueEntry[] Queue(int playlistindex, int trackindex)
        {
            if (Ready())
            {
                queueResult = false;
                Controller.Queue(playlistindex, trackindex);
                while (!queueResult) { if (!DoSomeWaiting()) { break; } }
            }
            return listQueue.ToArray();
        }

        public fbcQueueEntry[] QueueDel(int queueindex)
        {
            if (Ready())
            {
                queueResult = false;
                Controller.QueueDel(queueindex);
                while (!queueResult) { if (!DoSomeWaiting()) { break; } }
            }
            return listQueue.ToArray();
        }

        public fbcQueueEntry[] Queue(bool refresh)
        {
            if (refresh)
            {
                if (Ready())
                {
                    queueResult = false;
                    Controller.Queue();
                    while (!queueResult) { if (!DoSomeWaiting()) { break; } }
                }
            }
            return listQueue.ToArray();
        }

        public float Vol(bool refresh)
        {
            if (refresh)
            {
                if (Ready())
                {
                    volResult = false;
                    Controller.Vol();
                    while (!volResult) { if (!DoSomeWaiting()) { break; } }
                }
            }

            return vol;
        }

        public fbcOrderEnum Order(bool refresh)
        {
            if (refresh)
            {
                if (Ready())
                {
                    orderResult = false;
                    Controller.Order();
                    while (!orderResult) { if (!DoSomeWaiting()) { break; } }
                }
            }

            return order;
        }

        public void TrackInfoRefresh()
        {
            if (Ready())
            {
                trackinfoResult = false;
                Controller.TrackInfo();
                while (!trackinfoResult) { if (!DoSomeWaiting()) { break; } }
            }
        }
    }
}
