/*
    Copyright (C) 2001-2002 Paul Davis
    
    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 2 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, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    $Id: ksi.h,v 1.38 2004/06/24 05:58:29 jhall Exp $
*/

#ifndef __ksi_ksi_h__
#define __ksi_ksi_h__

#include <ncurses.h>
#include "compat.h"

#include <list>
#include <vector>
#include <string>
#include <stack>
#include <panel.h>
#include <glib.h>
#include <sigc++/signal_system.h>
#include <pthread.h>
#include <signal.h>
#include <pbd/receiver.h>
#include <jack/jack.h>
#include <ardour/route_group.h>
#include <ardour/session.h>
#include <ardour/location.h>
#include <ardour/panner.h>
#include <ardour/route.h>

using std::stack;

namespace MIDI {
	class Port;
}

namespace ARDOUR {
	class AudioEngine;
	class Redirect;
	class PluginInsert;
	class Route;
	class AudioTrack;
	class Session;
	class Location;
	class Locations;
	class RouteGroup;
}

class PhysicalKeyboard;

class KSI : public Receiver {

public:
    static const guint32 selection_size = 10;

    enum SelectionMode {
	    Group,
	    Route,
	    Plugin,
	    Insert,
	    Record,
	    RecordParameter,
	    Parameter,
	    IO,
	    Location,
	    Locations
    };

    KSI (ARDOUR::AudioEngine &engine, const string& server);
    ~KSI ();

    void set_master_lr_channels (string channels[2]);
    void get_master_lr_channels (string channels[2]);

    int run ();

    SelectionMode selection_mode() { return selection_mode_stack.top(); }

    static bool time_to_die () {
	    bool x;
	{
	PBD::LockMonitor ln (die_lock, __LINE__, __FILE__);
	    x = _die;
	}
	    return x;
    }

    static bool safe_to_save () {
	    bool x;
	{
	PBD::LockMonitor ln (die_lock, __LINE__, __FILE__);
		    x = _saveable;
	}
	    return x;
    }

    static void safe_to_save (bool x) {
	{
	PBD::LockMonitor ln (die_lock, __LINE__, __FILE__);
		    _saveable = x;
	}
	    return;
    }

    void add_group(ARDOUR::RouteGroup *);
    void _add_track(ARDOUR::Route&);
    void remove_group(ARDOUR::RouteGroup *);
    ARDOUR::Route *new_route(bool with_diskstream = false);
    ARDOUR::Route *control_route;

    int io_mode_selected(ARDOUR::RouteGroup *);
    void update_io_display();
    void inputs_selected();
    void _inputs_selected(ARDOUR::Route&);
    void outputs_selected();
    void _outputs_selected(ARDOUR::Route&);
    void route_signals (ARDOUR::Route *rt);

    void scrub();
    void _scrub(ARDOUR::Route&);
    float scrub_speed;
    bool _scrubbing;
    
  protected:
    void receive (Transmitter::Channel, const char *);
    
  private:
    enum SelectionType {
	    Add,
	    Replace,
	    Remove
    };
    
    enum OpType { 
	    none,
	    increment,
	    reset,
	    decrement
    };

    PhysicalKeyboard *keyboard;
    ARDOUR::AudioEngine& engine;
    bool paused;
    bool display_is_remote;
    bool need_control_room_outs;
    
    static bool _die;
    static bool _saveable;
    static PBD::Lock die_lock;
    static PBD::NonBlockingLock screen_lock;
    PBD::Lock butler_lock;
    pthread_cond_t cond;
    PBD::Lock group_lock;
    PBD::Lock selection_lock;
    PBD::Lock pan_lock;
    PBD::Lock gain_lock;
    PBD::Lock chns_lock;
    PBD::Lock choice_lock;
    PBD::Lock track_lock;
    
    ARDOUR::Session *session;
    ARDOUR::RouteGroup *current_group;
    ARDOUR::RouteGroup selection;
    list <ARDOUR::RouteGroup*> groups;
    ARDOUR::RouteGroup _track;
    
    vector<string> master_lr_channels;
    vector<string> control_lr_channels;
    vector<string> io_selection;
    bool _yn;

    int show_possible_inputs (int page);
    int show_possible_outputs (int page);
    int show_possible_ports (int page, unsigned long flags);
    void start_io_selection ();
    void start_input_selection();
    void start_output_selection();

    stack<SelectionMode> selection_mode_stack;

    void push_selection_mode (SelectionMode m);

    void ensure_selection_mode (SelectionMode m) {
	    if (selection_mode() != m) {
		    push_selection_mode (m);
	    }
    }
    
    void pop_selection_mode ();

    void shift_selection_right ();
    void shift_selection_left ();

    ARDOUR::pan_t p_amount;
    ARDOUR::gain_t g_amount;
    guint32 selection_cnt;
    guint32 group_cnt;
    guint32 track_cnt;
    guint32 plugin_page;
    guint32 insert_page;
    guint32 insert_cnt;
    ARDOUR::Route *_record_routeptr;
    guint32 _location_cnt;
    guint32 parameter_page;
    guint32 _location_page;
    guint32 route_start;
    guint32 io_start;
    guint32 io_page;

    void process_selection (guint32 which, SelectionType, OpType);
    void group_selected (guint32, SelectionType);
    void route_selected (guint32, SelectionType, ARDOUR::RouteGroup *);
    void io_selected(guint32, SelectionType);
    void plugin_selected (guint32, SelectionType);
    void insert_selected (guint32, SelectionType);
    void update_record_display(ARDOUR::Route *);
    void record_selected (guint32, SelectionType, OpType);
    void transport_rewind();
    void transport_forward();

    int key_press_handler (int keyboard_state, int key);

    void pan_left (ARDOUR::pan_t);
    void KSI::_pan_left(ARDOUR::Route&);
    void KSI::_pan_right(ARDOUR::Route&);
    void pan_right (ARDOUR::pan_t);
    void pan_hard_left ();
    void pan_hard_right ();

    void set_mute (bool yn);
    void _set_mute(ARDOUR::Route&);
    void set_solo (bool yn);
    void unset_solo (ARDOUR::Route&);
    void _set_solo(ARDOUR::Route&);

    void adjust_parameter (guint32, double);
    void adjust_gain (ARDOUR::Route&);
    void group_adjust_gain (ARDOUR::gain_t);
    void channel_adjust_gain (ARDOUR::gain_t);
    void unity_gain (ARDOUR::Route&);
    void channel_unity_gain ();
    void group_unity_gain ();
    void reset_parameter (guint32);

    void show_current_group ();
    void set_current_group (guint32 id);

    int show_possible_plugins (guint32 page);
    int show_parameters (guint32 page, ARDOUR::PluginInsert *);
    int show_inserts (guint32 page, bool show_active);
    void show_locations();
    int update_current_location_display();
    int smpte_time_str(char*, jack_nframes_t);
    void show_channel_range ();
    void show_insert (ARDOUR::Redirect *);
    
    bool insert_show_active;

    ARDOUR::PluginInsert *editing_insert;

    void add_plugin (ARDOUR::Route *, guint32);
    void edit_insert (ARDOUR::Route *, guint32);
    void remove_insert (ARDOUR::Route *, guint32);
    void set_insert_state (bool);

    void start_insert_edit ();
    void start_insert_add ();
    void start_insert_activate ();
    void remove_inserts ();
    void add_location();
    void remove_location();
    
    ARDOUR::Route *get_first_selected_route ();

    int get_route_channel (ARDOUR::Route *);

    /* UI */

    int initialize_ui ();

    void update_selection_display(ARDOUR::RouteGroup *);
    void _update_selection_display (ARDOUR::Route&);
    void update_group_content_display (ARDOUR::RouteGroup *);
    void _update_group_content_display (ARDOUR::Route&);
    void update_selection_mode_display ();
    void update_locations_display (ARDOUR::Locations::LocationList&);
    void _location_selected (ARDOUR::Locations::LocationList&);
    void location_selected(guint32 which);
    void update_channel_range_display ();
    void update_group_display (ARDOUR::RouteGroup *);
    void update_insert_display ();
    void update_transport_state_display ();

    WINDOW *lower_window;
    PANEL  *lower_panel;

    WINDOW *message_window;
    WINDOW *message_box;

    WINDOW *status_box;
    WINDOW *status_window;

    WINDOW *channels_box;
    WINDOW *channels_window;

    WINDOW *io_box;
    WINDOW *io_window;

    WINDOW *inserts_box;
    WINDOW *inserts_window;

    WINDOW *insert_box;
    WINDOW *insert_window;

    WINDOW *plugins_box;
    WINDOW *plugins_window;

    WINDOW *record_window;

    WINDOW *record_box;

    PANEL  *message_panel;
    PANEL  *route_panel;
    PANEL  *io_panel;
    PANEL  *inserts_panel;
    PANEL  *insert_panel;
    PANEL  *plugins_panel;
    PANEL *record_panel;
    WINDOW *locations_box;
    WINDOW *locations_window;
    PANEL  *locations_panel;

    PANEL  *my_top_panel;

    void ensure_panel_on_top (PANEL *);
    void redraw_screen (bool wait = true, bool hide_cursor = false);
    void totally_redraw_screen (void);

    gdouble channel_gain_unity_position;

    void route_gain_changed (void *src, ARDOUR::Route *mx, unsigned long chn) {
	    paint_channel_gain (chn, mx);
    }
    void route_mute_changed (void *src, ARDOUR::Route *mx, unsigned long chn) {
	    paint_channel_mute (chn, mx);
    }
    void route_solo_changed (void *src, ARDOUR::Route *mx, unsigned long chn) {
	    paint_channel_solo (chn, mx);
    }
    void route_record_enable_changed (void *src, ARDOUR::AudioTrack *mx, unsigned long chn) {
	    paint_channel_record_enable (chn, mx);
    }
    void session_record_enable_changed () {
	    update_transport_state_display();
    }
    void session_transport_state_change () {
	    update_transport_state_display();
    }

    void paint_channel_gain (unsigned long chn, ARDOUR::Route *);
    void paint_channel_mute (unsigned long chn, ARDOUR::Route *);
    void paint_channel_solo (unsigned long chn, ARDOUR::Route *);
    void paint_channel_record_enable (unsigned long chn, ARDOUR::AudioTrack *);

    int catch_signals (void);
    static void *butler_thread (void *);
    void start_butler_thread(void);
    static void *signal_thread (void *);
    static sigset_t signals;

    static void kill () { 
	PBD::LockMonitor lm(die_lock, __LINE__, __FILE__);
	    _die = true;
    }
    
    int tcp_connect (const string& host, int port);
};

#endif // __ksi_ksi_h__


