#include <time.h>
#include "tstream.hh"
#include "score.hh"
#include "pscore.hh"
#include "staff.hh"
#include "misc.hh"
#include "debug.hh"

void
Score::add(Command *c)
{
    commands_.bottom().add(new Command(*c));
}

void
Score::add(Staff*s)
{
    s->score_ = this;
    staffs_.bottom().add(s);    
}


void
Score::do_pcols()
{
    PCursor<Score_column*> sc(cols_);
    for (;sc.ok(); sc++) {
	pscore_->add(sc->pcol);
    }
}
/*
    this sux. Really makeshift.
    */
void
Score::do_miscs()
{
    Command c;

    c.when = 0.0;
    
    c.code = BREAK_END;
    commands_.top().insert(new Command(c));    
    c.code = BREAK_POST;
    commands_.top().insert(new Command(c));
    c.code = BREAK_MIDDLE;
    commands_.top().insert(new Command(c));
    c.code = BREAK_PRE;
    commands_.top().insert(new Command(c));

    PCursor<Command*> bot(commands_.bottom());
    c.when = last();    
    while (bot.ok() && bot->when > c.when) {
//       mtor <<"removing "<< bot->code <<" at " << bot->when<<'\n';
       bot.remove();
       bot = commands_.bottom();
    }
   
    c.code = BREAK_PRE;
    bot.add(new Command(c));
    bot++;
    c.code = BREAK_MIDDLE;
    bot.add(new Command(c));
   bot++;
    c.code = BREAK_POST;
    bot.add(new Command(c));
   bot++;
   c.code = BREAK_END;
    bot.add(new Command(c));
    
   commands_.OK();
}

Mtime
Score::last() const
{    
    Mtime l = 0;
    for (PCursor<Staff*> stc(staffs_); stc.ok(); stc++) {
	l = MAX(l, stc->last());
    }
    return l;
}
void
Score::clean_commands() 
{
    Mtime l= last();
    for (PCursor<Command*> cc(commands_); cc.ok(); cc++) {
	if (cc->when > l){
	   mtor << "remming \n";
	    cc.remove();
	}
    }
}
void
Score::process()
{
     do_miscs();
    
    /// distribute commands to disciples
    distribute_commands();
    
    pscore_ = new PScore;
    for (PCursor<Staff*> sc(staffs_); sc.ok(); sc++) {
	sc->set_output(pscore_);
	sc->process();
    }
    do_pcols();
    calc_idealspacing();
    clean_cols();
    OK();
    //    print();
    pscore_->calc_breaking();
}

// remove empty cols with no spacing attached.
/* should rethink ownership of cols
    */
void
Score::clean_cols()
{
    for (PCursor<Staff * > sc(staffs_); sc.ok(); sc++)
	sc->clean_cols();
    for (PCursor<Score_column*> c(cols_); c.ok(); c++) {
	if (!c->pcol->used) {
//	    mtor << "removing : "; c->print();
	    c.remove();
	}
    }
    
    pscore_->clean_cols();
}
/* this sux.  We should have Score_column create the appropriate PCol.
    Unfortunately, PCols don't know about their position.    
    */
// todo
PCursor<Score_column*>
Score::create_cols(Mtime w)
{
    Score_column* c1 = new Score_column(w);
    Score_column* c2 = new Score_column(w);
    
    c1->musical = false;
    c2->musical = true;
    
    PCursor<Score_column*> scc(cols_);

    for (; scc.ok(); scc++) {
	assert(scc->when != w);
	if (scc->when > w)
	    break;
    }

    if (!scc.ok()) {
	cols_.bottom().add(c1);
	cols_.bottom().add(c2);
	scc = cols_.bottom();
	scc --;
    } else {
	scc.insert(c1);
	scc.insert(c2);
	scc -= 2;
    }
    return scc;
}

Score_column*
Score::find_col(Mtime w,bool mus)
{
    PCursor<Score_column*> scc(cols_);
    for (; scc.ok(); scc++) {
	if (scc->when == w && scc->musical == mus)
	    return scc;
	if (scc->when > w)
	    break;
    }
    scc = create_cols(w);
    if (mus)
	scc++;
    return scc;
}

void
Score::distribute_commands(void)
{
    for (PCursor<Staff*> sc(staffs_); sc.ok(); sc++) {
	sc->add_commands(commands_);
    }
}


void
Score::output(String s)
{
    OK();
    mtor << "output to " << s << "...\n";

    Tex_stream the_output(s);
    the_output << "% Automatically generated by LilyPond 0.0 at";
    time_t t(time(0));
    the_output << ctime(&t)<<"\n";
    the_output << "% from input file ..\n";
    pscore_->output(the_output);
}

void
Score::OK() const
{
    for (PCursor<Staff*> sc(staffs_); sc.ok(); sc++) {
	sc->OK();
	assert(sc->score_ == this);
    }
    staffs_.OK();
    cols_.OK();
    for (PCursor<Score_column*> cc(cols_); cc.ok() && (cc+1).ok(); cc++) {
	assert(cc->when <= (cc+1)->when);
    }
    for (PCursor<Command*> cc(commands_); cc.ok() && (cc+1).ok(); cc++) {
	assert(cc->when <= (cc+1)->when);
    }
    
}

void
Score::print() const
{
    mtor << "score {\n"; 
    for (PCursor<Staff*> sc(staffs_); sc.ok(); sc++) {
	sc->print();
    }
    for (PCursor<Score_column*> sc(cols_); sc.ok(); sc++) {
	sc->print();
    }
    mtor << "}\n";
}

/****************************************************************/

Score_column::Score_column(Mtime w)
{
    when = w;
    pcol = new PCol(0);
    musical = false;
}

bool
Score_column::used() {
    return pcol->used;
}

void
Score_column::print() const
{
    mtor << "Score_column { mus "<< musical <<" at " <<  when<<'\n';
    mtor << " # symbols: " << pcol->its.size() << "\n";
    mtor << "durations: [" ;
    for (int i=0; i < durations.sz(); i++)
	mtor << durations[i] << " ";
    mtor << "]\n}\n";
}
