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

namespace MiniBar2k.Common
{
    class fbuiQueueBase { }

    class fbuiQueueState : fbuiQueueBase
    {
        public fbcThreadState state;
        public fbuiQueueState(fbcThreadState _state) { state = _state; }
    }

    class fbuiQueueList : fbuiQueueBase
    {
        public int list;
        public fbuiQueueList(int _list) { list = _list; }
    }

    class fbuiQueueItem : fbuiQueueBase
    {
        public int list;
        public int track;
        public fbuiQueueItem(int _list, int _track) { list = _list; track = _track; }
    }

    public delegate void fbuiFormEvent(object sender);

    class FoobarUI
    {
        private Queue<fbuiQueueBase> workqueue = new Queue<fbuiQueueBase>();
        private DataGridTableStyle styletable = null;
        private DataGridColumnStyle stylecolumn = null;
        private bool followplay = false;

        private FoobarController fbc = null;
        private FoobarPlaylist fbp = null;
        private DataGrid playlistgrid = null;

        private Label title = null;

        private MainMenu menu = null;
        private MenuItem foobar = null;
        private MenuItem foobarplaylist = null;
        private MenuItem foobarplaylistqueue = null;
        private MenuItem foobarorder = null;
        private MenuItem foobarorderdef = null;
        private MenuItem foobarorderrepeatplaylist = null;
        private MenuItem foobarorderrepeattrack = null;
        private MenuItem foobarordershuffletrack = null;
        private MenuItem foobarordershufflealbum = null;
        private MenuItem foobarordershuffledirectory = null;

        private MenuItem minibar = null;
        private MenuItem minibarconnect = null;
        private MenuItem minibarresync = null;
        private MenuItem minibardisconnect = null;
        private MenuItem minibarsettings = null;
        private MenuItem minibarabout = null;

        private ContextMenu contextmenu = null;
        private MenuItem playitem = null;
        private MenuItem enqueueitem = null;
        private MenuItem dequeueitem = null;

        private PictureBox stop = null;
        private PictureBox pause = null;
        private PictureBox play = null;
        private PictureBox prev = null;
        private PictureBox next = null;
        private PictureBox rand = null;
        private PictureBox home = null;
        private PictureBox album = null;
        private PictureBox search = null;

        private Panel status = null;

        private Timer ticktimer = null;

        public event fbuiFormEvent OnAbout;
        public event fbuiFormEvent OnSettings;
        public event fbuiFormEvent OnSearch;

        private MenuItem newMenuItem(String text)
        {
            MenuItem result = new MenuItem();
            result.Text = text;
            return result;
        }

        private MenuItem newMenuItem(String text, EventHandler onclick)
        {
            MenuItem result = new MenuItem();
            result.Text = text;
            result.Click += onclick;
            return result;
        }

        public FoobarUI(
                    FoobarController _fbc,
                    DataGrid _playlistgrid,
                    MainMenu _menu,
                    ContextMenu _contextmenu,
                    Label _title,
                    PictureBox _stop,
                    PictureBox _pause,
                    PictureBox _play,
                    PictureBox _prev,
                    PictureBox _next,
                    PictureBox _rand,
                    PictureBox _home,
                    PictureBox _album,
                    PictureBox _search,
                    Panel _status
            )
        {
            fbc = _fbc;
            playlistgrid = _playlistgrid;
            menu = _menu;
            contextmenu = _contextmenu;
            title = _title;
            stop = _stop;
            pause = _pause;
            play = _play;
            prev = _prev;
            next = _next;
            rand = _rand;
            home = _home;
            album = _album;
            search = _search;
            status = _status;

            fbp = new FoobarPlaylist(fbc);

            // BUILD MENU
            menu.MenuItems.Clear();

            foobar = newMenuItem("Foobar");
            foobarplaylist = newMenuItem("Playlists");
            foobarplaylistqueue = newMenuItem("Queue", playlistselect);
            foobarplaylist.MenuItems.Add(foobarplaylistqueue);
            foobar.MenuItems.Add(foobarplaylist);
            foobarorder = newMenuItem("Order");
            foobarorderdef = newMenuItem("Default", orderselect);
            foobarorderrepeatplaylist = newMenuItem("Repeat (playlist)", orderselect);
            foobarorderrepeattrack = newMenuItem("Repeat (track)", orderselect);
            foobarordershuffletrack = newMenuItem("Shuffle (tracks)", orderselect);
            foobarordershufflealbum = newMenuItem("Shuffle (albums)", orderselect);
            foobarordershuffledirectory = newMenuItem("Shuffle (directories)", orderselect);
            foobarorder.MenuItems.Add(foobarorderdef);
            foobarorder.MenuItems.Add(foobarorderrepeatplaylist);
            foobarorder.MenuItems.Add(foobarorderrepeattrack);
            foobarorder.MenuItems.Add(foobarordershuffletrack);
            foobarorder.MenuItems.Add(foobarordershufflealbum);
            foobarorder.MenuItems.Add(foobarordershuffledirectory);
            foobar.MenuItems.Add(foobarorder);
            menu.MenuItems.Add(foobar);
            
            minibar = newMenuItem("Minibar");
            minibarconnect = newMenuItem("Connect", connectclick);
            minibarresync = newMenuItem("Resync", connectclick);
            minibardisconnect = newMenuItem("Disconnect", disconnectclick);
            minibarsettings = newMenuItem("Setings...", settingsclick);
            minibarabout = newMenuItem("About...", aboutclick);
            minibar.MenuItems.Add(minibarconnect);
            minibar.MenuItems.Add(minibarresync);
            minibar.MenuItems.Add(minibardisconnect);
            minibar.MenuItems.Add(minibarsettings);
            minibar.MenuItems.Add(minibarabout);
            menu.MenuItems.Add(minibar);

            contextmenu.MenuItems.Clear();
            playitem = newMenuItem("Play", OnGridDoubleClick);
            enqueueitem = newMenuItem("Queue", OnQueue);
            dequeueitem = newMenuItem("Dequeue", OnDequeue);
            contextmenu.MenuItems.Add(playitem);
            contextmenu.MenuItems.Add(enqueueitem);
            contextmenu.MenuItems.Add(dequeueitem);
            contextmenu.Popup += OnPopup;

            // CONNECT BUTTONS
            stop.Click += stopclick;
            pause.Click += pauseclick;
            play.Click += playclick;
            prev.Click += prevclick;
            next.Click += nextclick;
            rand.Click += randclick;
            home.Click += homeclick;
            album.Click += albumclick;
            search.Click += searchclick;

            // SET GRID
            styletable = new DataGridTableStyle();
            styletable.MappingName = "FoobarPlaylist";
            stylecolumn = new DataGridTextBoxColumn();
            stylecolumn.MappingName = "Title";
            styletable.GridColumnStyles.Add(stylecolumn);
            playlistgrid.TableStyles.Add(styletable);
            ColumnResize();
            playlistgrid.Resize += OnGridResize;

            playlistgrid.ColumnHeadersVisible = false;
            playlistgrid.RowHeadersVisible = false;
            playlistgrid.BackColor = Color.White;
            playlistgrid.BackgroundColor = Color.White;
            playlistgrid.GridLineColor = Color.White;
            playlistgrid.HeaderForeColor = Color.White;

            playlistgrid.DoubleClick += OnGridDoubleClick;
            playlistgrid.ContextMenu = contextmenu;
           
            // SET STATUS
            SetButtonStates();

            // CONNECT EVENTS
            fbc.OnConnectionStateChange += OnConnectionStateChange;
            fbc.OnPlaylist += OnPlaylist;
            fbc.OnOrder += OnOrder;
            fbc.OnSync += OnSync;
            fbc.OnPlay += OnPlay;

            // SET TIMER
            ticktimer = new Timer();
            ticktimer.Interval = 50;
            ticktimer.Tick += OnTick;
            ticktimer.Enabled = true;
        }

        private void OnTick(object sender, EventArgs e)
        {
            fbc.Tick();

            while (workqueue.Count > 0)
            {
                fbuiQueueBase entry = workqueue.Dequeue();

                if (entry is fbuiQueueState)
                {
                    SetButtonStates();
                    if (((fbuiQueueState)entry).state == fbcThreadState.connecting)
                    {
                        status.BackColor = Color.Yellow;
                    }

                    if (!fbc.isConnected()) { title.Text = ""; }
                }

                if (entry is fbuiQueueList)
                {
                    if (playlistgrid.DataSource != null) { playlistgrid.DataSource = null; }
                    fbuiQueueList queue = (fbuiQueueList)entry;
                    if (queue.list == -1)
                    {
                        fbc.Manager.Queue(true);
                    }
                    fbp.Become(queue.list);
                    playlistgrid.DataSource = fbp;
                }

                if (entry is fbuiQueueItem)
                {
                    fbuiQueueItem queue = (fbuiQueueItem)entry;
                    if (playlistgrid.DataSource == null) {
                        fbp.Become(queue.list);
                        playlistgrid.DataSource = fbp; 
                    }
                    playlistgrid.CurrentRowIndex = queue.track - 1;
                }
            }
        }

        private void OnConnectionStateChange(object sender, fbcThreadState state)
        {
            workqueue.Enqueue(new fbuiQueueState(state));
        }

        private void OnPlay(object sender, fbcPlayState state, fbcPlayEntry entry)
        {
            title.Text = fbc.FormatTitle(entry.PlaylistIndex, entry.TrackIndex, false);

            if (followplay)
            {
                GoToCurrent();
                followplay = false;
            }

            for (int i = 0; i < foobarplaylist.MenuItems.Count; i++)
            {
                foobarplaylist.MenuItems[i].Checked = (i - 1 == entry.PlaylistIndex);
            }

            QueueRefreshIfNeeded();
        }

        private void OnSync(object sender, bool isafter)
        {
            if (!isafter)
            {
                playlistgrid.DataSource = null;
            }
            else
            {
                OnConnectionStateChange(this, fbcThreadState.connected);
            }
        }

        private void OnPlaylist(object sender, fbcPlaylist[] lists, int currentindex)
        {
            foobarplaylist.MenuItems.Clear();
            foobarplaylist.MenuItems.Add(foobarplaylistqueue);
            
            MenuItem item = null;
            foreach (fbcPlaylist list in lists)
            {
                item = newMenuItem(list.Title, playlistselect);
                item.Checked = (list.PlaylistIndex == currentindex);
                foobarplaylist.MenuItems.Add(item);
            }
        }

        private void OnOrder(object sender, fbcOrderEnum order)
        {
            foobarorderdef.Checked =                (order == fbcOrderEnum.def);
            foobarorderrepeatplaylist.Checked =     (order == fbcOrderEnum.repeatplaylist);
            foobarorderrepeattrack.Checked =        (order == fbcOrderEnum.repeattrack);
            foobarordershuffletrack.Checked =       (order == fbcOrderEnum.shuffletrack);
            foobarordershufflealbum.Checked =       (order == fbcOrderEnum.shufflealbum);
            foobarordershuffledirectory.Checked =   (order == fbcOrderEnum.shuffledirectory);
        }

        private void OnGridResize(object sender, EventArgs e)
        {
            ColumnResize();
        }

        private void OnGridDoubleClick(object sender, EventArgs e)
        {
            if (playlistgrid.CurrentRowIndex < fbp.Count)
            {
                FoobarPlaylistItem entry = (FoobarPlaylistItem)fbp[playlistgrid.CurrentRowIndex];
                fbc.Play(entry.PlaylistIndex, entry.TrackIndex);
            }
        }

        private void OnQueue(object sender, EventArgs e) 
        {
            if (playlistgrid.CurrentRowIndex < fbp.Count)
            {
                FoobarPlaylistItem entry = (FoobarPlaylistItem)fbp[playlistgrid.CurrentRowIndex];
                fbc.Manager.Queue(entry.PlaylistIndex, entry.TrackIndex);
                QueueRefreshIfNeeded();
            }
        }

        private void OnDequeue(object sender, EventArgs e) 
        {
            if (playlistgrid.CurrentRowIndex < fbp.Count)
            {
                FoobarPlaylistItem entry = (FoobarPlaylistItem)fbp[playlistgrid.CurrentRowIndex];
                fbc.Manager.QueueDel(entry.QueueIndex);
                QueueRefreshIfNeeded();
            }
        }

        private void OnPopup(object sender, EventArgs e) 
        {
            playitem.Enabled = fbc.isConnected();
            enqueueitem.Enabled = ((fbp.lastplaylist != -1) && (fbc.isConnected()));
            dequeueitem.Enabled = ((fbp.lastplaylist == -1) && (fbc.isConnected()));
        }

        private void QueueRefreshIfNeeded()
        {
            if (fbp.lastplaylist == -1)
            {
                playlistgrid.DataSource = null;
                workqueue.Enqueue(new fbuiQueueList(-1));
            }
        }

        private void ColumnResize()
        {
            stylecolumn.Width = playlistgrid.Size.Width - 15;
        }

        private void SetButtonStates()
        {
            stop.Enabled =
                pause.Enabled =
                play.Enabled =
                prev.Enabled =
                next.Enabled =
                rand.Enabled =
                home.Enabled =
                album.Enabled =
                search.Enabled =
                foobar.Enabled =
                minibarresync.Enabled =
                minibardisconnect.Enabled = 
                    fbc.isConnected();

            minibarconnect.Enabled =
                    !fbc.isConnected();

            status.BackColor = fbc.isConnected() ? Color.Lime : Color.Red;

            if ((fbc.isConnected()) && (playlistgrid.DataSource == null))
            {
                fbp.Become(fbc.Manager.CurrentPlaylist);
                playlistgrid.DataSource = fbp;
            }

            if ((!fbc.isConnected()) && (playlistgrid.DataSource != null))
            {
                playlistgrid.DataSource = null;
            }
        }

        private void playlistselect(Object sender, EventArgs e)
        {
            int curlist = 0;

            for (int i = 0; i < foobarplaylist.MenuItems.Count; i++)
            {
                if (foobarplaylist.MenuItems[i] == sender)
                {
                    foobarplaylist.MenuItems[i].Checked = true;
                    curlist = i - 1;
                }
                else 
                {
                    foobarplaylist.MenuItems[i].Checked = false;
                }
            }

            playlistgrid.DataSource = null;
            workqueue.Enqueue(new fbuiQueueList(curlist));
        }

        private void GoToCurrent()
        {
            if (fbc.Manager.CurrentTrack.PlaylistIndex != fbp.lastplaylist) { playlistgrid.DataSource = null; }
            workqueue.Enqueue(new fbuiQueueItem(fbc.Manager.CurrentTrack.PlaylistIndex, fbc.Manager.CurrentTrack.TrackIndex)); 
        }

        private void FollowPlay()
        {
            followplay = true;
        }

        private void settingsclick(Object sender, EventArgs e)
        {
            OnSettings(this);
        }

        private void aboutclick(Object sender, EventArgs e)
        {
            OnAbout(this);
        }

        private void orderselect(Object sender, EventArgs e) 
        {
            if (sender == foobarorderdef)               { fbc.Order(fbcOrderEnum.def); OnOrder(this, fbcOrderEnum.def); }
            if (sender == foobarorderrepeatplaylist)    { fbc.Order(fbcOrderEnum.repeatplaylist); OnOrder(this, fbcOrderEnum.repeatplaylist); }
            if (sender == foobarorderrepeattrack)       { fbc.Order(fbcOrderEnum.repeattrack); OnOrder(this, fbcOrderEnum.repeattrack); }
            if (sender == foobarordershuffletrack)      { fbc.Order(fbcOrderEnum.shuffletrack); OnOrder(this, fbcOrderEnum.shuffletrack); }
            if (sender == foobarordershufflealbum)      { fbc.Order(fbcOrderEnum.shufflealbum); OnOrder(this, fbcOrderEnum.shufflealbum); }
            if (sender == foobarordershuffledirectory)  { fbc.Order(fbcOrderEnum.shuffledirectory); OnOrder(this, fbcOrderEnum.shuffledirectory); }
        }

        private void homeclick(Object sender, EventArgs e) { GoToCurrent(); }

        private void albumclick(Object sender, EventArgs e)
        {
            fbc.ShowAlbums(!fbc.ShowAlbums(null)); 
            playlistgrid.Invalidate();
            title.Text = fbc.FormatTitle(fbc.Manager.CurrentTrack.PlaylistIndex, fbc.Manager.CurrentTrack.TrackIndex, false);
        }

        private void searchclick(Object sender, EventArgs e) { OnSearch(this);  }

        private void connectclick(Object sender, EventArgs e) { fbc.Resync(); if (fbc.isConnected()) { GoToCurrent();  } }

        private void disconnectclick(Object sender, EventArgs e) { fbc.Exit(); }

        private void stopclick(Object sender, EventArgs e) { FollowPlay(); fbc.Stop(); }

        private void pauseclick(Object sender, EventArgs e) { FollowPlay(); fbc.Pause(); }

        private void playclick(Object sender, EventArgs e) { FollowPlay(); fbc.Play(null, null); }

        private void prevclick(Object sender, EventArgs e) { FollowPlay(); fbc.Prev(null); }

        private void nextclick(Object sender, EventArgs e) { FollowPlay(); fbc.Next(null); }

        private void randclick(Object sender, EventArgs e) { FollowPlay(); fbc.Rand(null); }



        // Search functionality
        private Form searchform = null;
        private TextBox searchtext = null;
        private Button searchbutton = null;
        private ListBox searchbox = null;
        private PictureBox searchdisplay = null;
        private fbcBaseEntry[] searchresults = null;
        private int searchdisplaymode = 0;
        private ContextMenu searchmenu = null;
        private MenuItem searchmenuplay = null;
        private MenuItem searchmenuqueue = null;

        public void Search(Form _searchform, TextBox _searchtext, Button _searchbutton, ListBox _searchbox, PictureBox _searchdisplay, ContextMenu _searchmenu)
        {
            searchform = _searchform;
            searchtext = _searchtext;
            searchbutton = _searchbutton;
            searchbox = _searchbox;
            searchdisplay = _searchdisplay;
            searchmenu = _searchmenu;

            searchdisplaymode = 0;
            searchresults = new fbcBaseEntry[0];

            searchmenuplay = newMenuItem("Play", searchmenuplayclick);
            searchmenuqueue = newMenuItem("Queue", searchmenuqueueclick);
            searchmenu.MenuItems.Add(searchmenuplay);
            searchmenu.MenuItems.Add(searchmenuqueue);

            searchtext.KeyPress += searchtextkeypress;
            searchbutton.Click += searchbuttonclick;
            searchdisplay.Click += searchdisplayclick;
            searchform.Closing += searchformclosing;
            searchmenu.Popup += searchmenupopup;
        }

        private String FormatSearch(fbcBaseEntry entry)
        {
            // 0 = (Time) Artist - [Album #track] Title
            // 1 = (Time) Artist - Title
            // 2 = (Time) Title

            StringBuilder sb = new StringBuilder();
            int minutes = 0;
            int seconds = 0;
            String length = "";
            int baselength = 0;

            minutes = (int)(entry.LengthSec / 60);
            seconds = (int)(entry.LengthSec % 60);

            length = minutes.ToString();
            while (length.Length < 2) { length = "0" + length; }
            sb.Append(length);
            sb.Append(":");
            length = seconds.ToString();
            while (length.Length < 2) { length = "0" + length; }
            sb.Append(length);
            sb.Append(" ");
            baselength = sb.Length;

            if ((entry.Artist.Length > 0) && (searchdisplaymode <= 1))
            {
                sb.Append(entry.Artist);
                sb.Append(" - ");
            }

            if ((entry.Album.Length > 0) && (searchdisplaymode == 0))
            {
                sb.Append("[");
                sb.Append(entry.Album);
                if (entry.TrackNumber != null)
                {
                    sb.Append(" #");
                    sb.Append(entry.TrackNumber.ToString());
                }
                sb.Append("] ");
            }

            if (entry.Title.Length > 0)
            {
                sb.Append(entry.Title);
            }
            else
            {
                if (sb.Length > baselength)
                {
                    sb.Append("<no title>");
                }
                else
                {
                    sb.Append(entry.RawPath);
                }
            }

            return sb.ToString();
        }

        private void DisplaySearchResults()
        {
            searchbox.BeginUpdate();
            if (searchresults != null)
            {
                if (searchresults.Length > 0)
                {
                    for (int i = 0; i < searchresults.Length; i++)
                    {
                        if (searchbox.Items.Count > i)
                        {
                            searchbox.Items[i] = FormatSearch(searchresults[i]);
                        }
                        else
                        {
                            searchbox.Items.Add(FormatSearch(searchresults[i]));
                        }
                    }
                }
            }
            searchbox.EndUpdate();
        }

        private void PerformSearch()
        {
            searchbox.Items.Clear();
            if (searchtext.Text.Length > 0) {
                if (fbc.isConnected())
                {
                    searchresults = fbc.Manager.Search(fbc.Manager.CurrentPlaylist, searchtext.Text);
                    DisplaySearchResults();
                }
            }
        }

        private void searchdisplayclick(object sender, EventArgs e)
        {
            searchdisplaymode++;
            if (searchdisplaymode == 3) { searchdisplaymode = 0; }
            DisplaySearchResults();
        }

        private void searchtextkeypress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)13) 
            {
                e.Handled = true;
                PerformSearch();
            }
        }

        private void searchbuttonclick(object sender, EventArgs e)
        {
            PerformSearch();
        }

        private void searchmenuplayclick(object sender, EventArgs e)
        {
            if (searchbox.SelectedIndex >= 0)
            {
                fbcBaseEntry entry = searchresults[searchbox.SelectedIndex];
                searchform.Close();
                FollowPlay();
                fbc.Play(entry.PlaylistIndex, entry.TrackIndex);
            }
        }

        private void searchmenuqueueclick(object sender, EventArgs e)
        {
            if (searchbox.SelectedIndex >= 0)
            {
                fbcBaseEntry entry = searchresults[searchbox.SelectedIndex];
                fbc.Manager.Queue(entry.PlaylistIndex, entry.TrackIndex);
                QueueRefreshIfNeeded();
            }
        }

        private void searchmenupopup(object sender, EventArgs e)
        {
            searchmenuplay.Enabled =
                searchmenuqueue.Enabled =
                    (searchbox.SelectedIndex >= 0);
        }

        private void searchformclosing(object sender, EventArgs e)
        {
            searchform = null;
            searchtext = null;
            searchbutton = null;
            searchbox = null;
            searchdisplay = null;
            searchresults = null;
            searchdisplaymode = 0;
            searchmenu = null;
            searchmenuplay = null;
            searchmenuqueue = null;

            GC.Collect();
        }
    }
}
