#include "scommands.hh"
#include "tstream.hh"
#include "score.hh"
#include "sccol.hh"
#include "pscore.hh"
#include "staff.hh"
#include "debug.hh"
#include "paper.hh"

void
Score::set(Paperdef*p)
{
    delete paper_;
    paper_ = p;
}
void
Score::output(String s)
{
    OK();
    if (paper_->outfile=="")
	paper_->outfile = s;
    
    *mlog << "output to " << paper_->outfile << "...\n";
    Tex_stream the_output(paper_->outfile);    
    pscore_->output(the_output);
}


void
Score::process()
{
    *mlog << "Processing ...";
    set(commands_->parse(last()));
    commands_->print();
    
    if (!paper_)
	paper_ = new Paperdef;
    
    commands_->clean(last());
    
    /// distribute commands to disciples
    distribute_commands();
    
    pscore_ = new PScore(paper_);
    for (PCursor<Staff*> sc(staffs_); sc.ok(); sc++) {
	sc->set_output(pscore_);
	sc->process();
    }

    // do this after processing, staffs first have to generate PCols.
    do_pcols();
    calc_idealspacing();
    clean_cols();
    OK();
    //    print();
    *mlog << "Calculating ... ";
    pscore_->calc_breaking();
    // TODO: calculate vertical structs
    // TODO: calculate mixed structs.
    *mlog << "\n";
}

// 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(); ) {
	if (!c->pcol->used) {
	    mtor << "removing : ";
	    c->print();
	    c.del();
	} else
	    c++;
    }
    
    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(Real 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(Real 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::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);
    }
}
Real
Score::last() const
{    
    Real l = 0;
    for (PCursor<Staff*> stc(staffs_); stc.ok(); stc++) {
	l = MAX(l, stc->last());
    }
    return l;
}

void
Score::OK() const
{
#ifndef NDEBUG
    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);
    }
    commands_->OK();
#endif    
}


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

Score::Score()
{
    pscore_=0;
    paper_ = 0;
    commands_ = new Score_commands;
}

Score::~Score()
{
    delete pscore_;
    delete commands_;
    delete paper_;
}

void
Score::set(Score_commands*c)
{
    delete commands_;
    commands_ = c;
}
