// utility functions for PScore
#include "debug.hh"
#include "lookup.hh"
#include "spanner.hh"
#include "paper.hh"
#include "molecule.hh"
#include "dimen.hh"
#include "scoreline.hh"
#include "pscore.hh"
#include "tstream.hh"


void
PScore::clean_cols()
{
    for (PCursor<PCol *> c(cols); c.ok(); )
	if (!c->used()) {
	    c.del();
	} else
	    c++;
}


void
PScore::add(PStaff *s)
{
    assert(s->pscore_ == this);
    staffs.bottom().add(s);
}

void
PScore::typeset_item(Item *i, PCol *c, PStaff *s, int breakstat)
{
    assert(c && i && s);
//    assert(!breakstat != 4 || c->breakable() );
    if (breakstat == 0) {
	typeset_item(i, c->prebreak, s);
	return;
    }

    if (breakstat == 2) {
	typeset_item(i, c->postbreak, s);
	return;
    }


    its.bottom().add(i);
    s->add(i);
    c->add(i);

    /* first do this, because i->width() may follow the 0-pointer */
    i->preprocess();


    if (0 && c->daddy && c == c->daddy->prebreak) { // makeshift.
	
	Interval iv (i->width());
	if (!iv.empty()) {
	    svec<Item*> col_its (select_items(s, c));
	    for (int j =0; j < col_its.sz(); j++)
		col_its[j]->translate(Offset(-iv.length(),0));
	    i->translate (Offset(-iv.max, 0));
	}
    }
    
}

void
PScore::typeset_spanner(Spanner*sp, PStaff*ps)
{
    sp->preprocess();
    sp->pstaff_ = ps;
    spanners.bottom().add(sp);
    ps->spans.bottom().add(sp);
    // do not init start/stop fields. These are for broken spans only.
}


void
PScore::add_line(svec<const PCol *> curline, svec<Real> config)
{    
    Line_of_score *p = new Line_of_score(curline,this);
    lines.bottom().add(p);   	
    for (int i=0; i < curline.sz(); i++){
	PCol *c=(PCol *)curline[i]; // so, this isn't really const.
	c->hpos = config[i];
    }
}

Idealspacing*
PScore::get_spacing(PCol*l, PCol*r)
{
    assert(l!=r);
    for (PCursor<Idealspacing*> ic (suz); ic.ok(); ic++) {
	if (ic->left == l && ic->right == r){
	    return ic;
	}
    }
    
    Idealspacing*ip =new Idealspacing(l,r);
    suz.bottom().add(ip);

    return ip;
}


int
PScore::compare_pcols(const PCol*a, const PCol*b)const
{
    PCursor<PCol*> ac(find_col(a));
    PCursor<PCol*> bc(find_col(b));
    assert(ac.ok() && bc.ok());
    return ac - bc;
}

/*
  return all breakable columns
 */
svec<const PCol *>
PScore::find_breaks() const
{
    svec<const PCol *> retval;
    for (PCursor<PCol *> c(cols); c.ok(); c++)
	if (c->breakable())
	    retval.add(c);

    return retval;
}

void
PScore::add(PCol *p)
{
    p->pscore_ = this;
    if (p->breakable()){
	p->prebreak->pscore_ = this;
	p->postbreak->pscore_ = this;
    }
    cols.bottom().add(p);
}

PScore::PScore( Paperdef*p)
{
    paper_ = p;
}

void
PScore::output(Tex_stream &ts)
{
    int l=1;

    ts << "\n "<<  paper_->lookup_->texsetting << "%(Tex id)\n";
    for (PCursor<Line_of_score*> lic(lines); lic.ok(); lic++) {
	ts << "% line of score no. " << l++ <<"\n";
	ts << lic->TeXstring();
	if ((lic+1).ok())
	    ts << "\\interscoreline\n";
    }	
}


svec<Item*>
PScore::select_items(PStaff*ps , PCol*pc)
{
    svec<Item*> ret;
    assert(ps && pc);
    for (PCursor<const Item*> ic(pc->its); ic.ok(); ic++){
	if (ic->pstaff_ == ps)
	    ret.add((Item*)(const Item*)ic);
    }
    return ret;
}

void
PScore::OK()const
{
#ifdef NDEBUG
    for (PCursor<PCol*> cc(cols); cc.ok(); cc++)
	cc->OK();
    for (PCursor<Idealspacing*> ic(suz); ic.ok(); ic++)
	ic->OK();
#endif
}

void
PScore::print() const
{    
#ifndef NPRINT
    mtor << "PScore { ";
    paper_->print();
    mtor << "\ncolumns: ";
    for (PCursor<PCol*> cc(cols); cc.ok(); cc++)
	cc->print();
    
    mtor << "\nideals: ";
    for (PCursor<Idealspacing*> ic(suz); ic.ok(); ic++)
	ic->print();
    mtor << "}\n";
#endif 
}

void
PScore::preprocess()
{
}

void
PScore::postprocess()
{
    for (PCursor<Spanner*> ic(broken_spans); ic.ok(); ic++) {
	ic->process();
    }
    for (PCursor<Item*> ic(its); ic.ok(); ic++){
	ic->postprocess();
    }
    for (PCursor<Line_of_score*> i(lines); i.ok(); i++)
	i->process();
}

PCursor<PCol *>
PScore::find_col(const PCol *c)const
{
    PCol*what = (PCol*)c;
    if (what->daddy )
	what = what -> daddy;
    
    return cols.find(what);
}

void
PScore::add_broken(Spanner*s)
{
    broken_spans.bottom().add(s);
    s->left->starters.bottom().add (s);
    s->right->stoppers.bottom().add (s);
}
