/*
 * =====================================================================================
 * 
 *        Filename:  mposd.cpp
 * 
 *     Description:  A OSD display for MPD.
 * 
 *         Version:  1.0
 *         Created:  10/12/04 12:47:56 EDT
 *        Revision:  none
 *        Compiler:  g++
 * 
 *          Author:   (Natan Zohar), nzohar1ATbinghamtonDOTedu
 *         Company:  
 * 
 * =====================================================================================
 */

/* This is a very basic client for mpd which uses xosd to display the currently playing song on screen, 
 * the option parsing is also very limited and alot of work needs to be done on it. Also, Jethro Tull is 
 * the best music to code to. Ever.
 */

#include <ctime>
#include <cstdlib>
#include <string>
#include <iostream>
#include <fstream>
#include <sys/select.h>
#include <getopt.h>
#include <xosd.h>
#include "libmpdclient.h"

#define VERSION "0.0.1"

using namespace std;

int write_to_stderr(string message) {
    write(2, message.c_str(), message.length());
    return 0;
}

int write_to_file(string message, string some_file, bool to_stdout) {
    if (!to_stdout) {
        ofstream outfile_writer(some_file.c_str(), ios::app);
        if (outfile_writer.fail()) {
            write_to_stderr("Couldn't open file for writing");
        } else {
            outfile_writer << message;
        }
        outfile_writer.close();
    } else {
        cout << message;
    }
    return 0;
}

string display_help() {
    return "mposd " + string(VERSION) + "\nUsage: mposd [OPTION] ... \n\n \
        -v, --version           display version\n \
        -h, --help              display this message\n\n \
        \
        Need to finish writing this.";
}

int main(int argc, char **argv) {
    string outfile;
    bool to_stdout = true;

    time_t local_time;
    
    mpd_Status *mpd_status;
    mpd_Connection *my_mpd;
    mpd_Song *mpd_song;
    mpd_InfoEntity *mpd_info_entity;
    int cur_song_id = -1,read_song_id;
    long cur_playlist_version = -1, read_playlist_version;
    string host = "localhost";
    int port = 6600;

    char *font = "-adobe-utopia-bold-*-*-*-*-180-*-100-*-140-iso8859-1";
    string font_color = "white";
    int xosd_song_timeout = 3, xosd_status_timeout = 1;
    int shadow_offset = 0;
    xosd* my_osd;
    
    string disp_text; // string to keep sending to XOSD
    string artist = "", title = "", status = "";
    string read_status;
    bool bool_display_state = true;
    bool bool_display_song = true;

    int seconds = 3;
    struct timeval *poll_timeout = new struct timeval;
    
    
    // OPTION PARSING
    char c;
    while (true) {
        int option_index = 0;
        static struct option long_options[] = {
            {"osd-color", 1, 0, 'c'},
            {"font", 0, 0, 'f'},
            {"shadow", 0, 0, 's'}, 
            {"options-file", 1, 0, 'i'},
            {"logfile", 1, 0, 'o'},
            {"poll-time", 1, 0, 't'},
            {"host", 1, 0, 'n'},
            {"port", 1, 0, 'p'},
            {"help", 0, 0, 'h'},
            {"version", 0, 0, 'v'}, 
            {0, 0, 0, 0}
        };

        c = getopt_long (argc, argv, "t:o:c:s:g:hv012",
                long_options, &option_index);
        if (c == -1)
            break;

        switch (c) {
            case 0:
                cout << long_options[option_index].name << endl;
                if (optarg)
                    cout << optarg << endl;
                break;
            // change font color
            case 'c':
                font_color = optarg;
                break;
            // show help
            case 'h':
                // write a better help function
                write_to_stderr(display_help());
                return 0;
            // options file
            case 'i': 
                // to implement
                break;
            // hostname
            case 'n':
                host = optarg;
                break;
            // output file (no option to send to stdout)
            case 'o':
                outfile = optarg;
                to_stdout = false;
                break;
            // set port num
            case 'p':
                port = atoi(optarg);
                break;
            // shadow offset
            case 's':
                shadow_offset = atoi(optarg);
                break;
            // poll time on mpd
            case 't':
                seconds = atoi(optarg);
                break;
            // set font name
            case 'f':
                font = optarg;
                break;
            // show version
            case 'v':
                write_to_stderr(string(VERSION));
                return 0;
            default:
                return 1;
        }
    }
    // END OPTION PARSING
    
    poll_timeout->tv_sec = seconds;
    poll_timeout->tv_usec = 0;    
    
    my_osd = xosd_create(2);
    xosd_set_horizontal_offset(my_osd, 0);
    xosd_set_vertical_offset(my_osd, 0);
    xosd_set_font(my_osd, font);
    xosd_set_pos(my_osd, XOSD_bottom);
    xosd_set_align(my_osd, XOSD_right);
    xosd_set_colour(my_osd, font_color.c_str());
    xosd_display(my_osd, 0, XOSD_string, "blah..."); 
    // check if shadow offset is defined
    if (shadow_offset > 0) {
        xosd_set_shadow_offset(my_osd, shadow_offset);
    }
    
    // connect to mpd
    my_mpd = mpd_newConnection(host.c_str(), port, 10);
    if (my_mpd->error) {
        cerr << "Error in connecting to mpd at " << host << ":" << port << endl;
        exit(-1);
    }
    
    while (true) {
        mpd_sendCommandListOkBegin(my_mpd);
        mpd_sendStatusCommand(my_mpd);
        mpd_sendCurrentSongCommand(my_mpd);
        mpd_sendCommandListEnd(my_mpd);
        mpd_status = mpd_getStatus(my_mpd);
        mpd_nextListOkCommand(my_mpd);
        mpd_info_entity = mpd_getNextInfoEntity(my_mpd);
        mpd_finishCommand(my_mpd);
        if (mpd_info_entity == NULL) {
            write_to_stderr("No Song is selected\n");
        } else {
            mpd_song = mpd_info_entity->info.song;
            read_song_id = mpd_status->song;
        
            if (mpd_status->state == MPD_STATUS_STATE_PLAY) {
                read_status = "Playing";
            } else if (mpd_status->state == MPD_STATUS_STATE_STOP) {
                read_status = "Stopped";
            } else {
                read_status = "Unknown";
            }

            if (cur_song_id != read_song_id || cur_playlist_version != read_playlist_version) {
                bool_display_song = true;
                artist = mpd_song->artist;
                title = mpd_song->title;
                cur_song_id = read_song_id;
                cur_playlist_version = read_playlist_version;
            }
            if (read_status != status) {
                bool_display_state = true;
                status = read_status;
            }


            if (bool_display_state) {
                disp_text = status;
                time(&local_time);
                write_to_file(disp_text + ": " + ctime(&local_time), outfile, to_stdout);
                xosd_set_timeout(my_osd, xosd_status_timeout);
                xosd_display(my_osd, 0, XOSD_string, disp_text.c_str());
                while (xosd_is_onscreen(my_osd)) {
                    poll_timeout->tv_usec = 500;
                    select(0, NULL, NULL, NULL, poll_timeout);
                    continue;
                }
                bool_display_state = false;
            }
            if (bool_display_song) {
                disp_text = artist + " - " + title;
                time(&local_time); 
                write_to_file(disp_text + ": " + ctime(&local_time), outfile, to_stdout);
                xosd_set_timeout(my_osd, xosd_song_timeout);
                xosd_display(my_osd, 0, XOSD_string, "Now Playing:");
                xosd_display(my_osd, 1, XOSD_string, disp_text.c_str());
                while (xosd_is_onscreen(my_osd)) {
                    poll_timeout->tv_usec = 500;
                    select(0, NULL, NULL, NULL, poll_timeout);
                    continue;
                }
                bool_display_song = false;
            }




        }
        poll_timeout->tv_sec = seconds; // make it wait another set of seconds, since select changes the timeout val
        select(0, NULL, NULL, NULL, poll_timeout);
    }
    xosd_destroy(my_osd);
    
}
