/** \file
 \brief Contains the TRestrictionEditor class members
 */
#include "TRestrictionEditor.h"

BEGIN_EVENT_TABLE(TRestrictionEditor, wxDialog )
EVT_LIST_ITEM_SELECTED(RSE_RES_LL, TRestrictionEditor::res_ll)
EVT_LIST_ITEM_ACTIVATED(RSE_RES_LL, TRestrictionEditor::res_ll_act)
EVT_CHOICE(RSE_RES_DD,TRestrictionEditor::res_dd)
EVT_CHECKBOX(RSE_COC_CB,TRestrictionEditor::res_checkbox)
EVT_CHECKBOX(RSE_ADD2GEL,TRestrictionEditor::res_cb_add2gel)
EVT_CHECKBOX(RSE_PARTIAL_DIGESTION,TRestrictionEditor::res_cb_add2gel)
EVT_RADIOBOX(RSE_RES_RB,TRestrictionEditor::res_cb)
EVT_BUTTON(RSE_RES_AC,TRestrictionEditor::res_ac)
EVT_BUTTON(RSE_COC_CT,TRestrictionEditor::res_ct)
EVT_BUTTON(RSE_COC_RM,TRestrictionEditor::res_coc_rm)
EVT_BUTTON(RSE_RES_OK,TRestrictionEditor::onOK)
EVT_BUTTON(RSE_RES_CC,TRestrictionEditor::onCancel)
EVT_BUTTON(RSE_ADD_ALL,TRestrictionEditor::onAddAll)
EVT_LIST_ITEM_SELECTED(RSE_COC_LL, TRestrictionEditor::res_coc_ll)
EVT_LIST_COL_CLICK(RSE_RES_LL,TRestrictionEditor::res_ll_col)
EVT_CHAR_HOOK(TRestrictionEditor::OnCharHook)
END_EVENT_TABLE()

using namespace std ;

#define BY_NAME 0
#define BY_CUTS 1

int global_sort_mode ;
bool global_sort_ascending ;

bool operator < ( const TREcache& c1 , const TREcache& c2 )
{
    if ( global_sort_mode == BY_NAME )
        return global_sort_ascending?(c1.enzyme<c2.enzyme):(c1.enzyme>c2.enzyme);
    else if ( global_sort_mode == BY_CUTS )
        return global_sort_ascending?(c1.cut<c2.cut):(c1.cut>c2.cut);

    fprintf(stderr,"E: This should not be reached in bool operator < ( const TREcache c1 , const TREcache c2 )\n");
    assert(0);
    return 0;
}

bool operator == ( const TREcache& c1 , const TREcache& c2 )
    {
    if ( global_sort_mode == BY_NAME )
        return c1.enzyme == c2.enzyme ;
    else if ( global_sort_mode == BY_CUTS )
        return c1.cut == c2.cut ;

    fprintf(stderr,"E: This should not be reached in bool operator == ( const TREcache c1 , const TREcache c2 )\n");
    assert(0);
    return 0;
    }

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


void TRestrictionEditor::OnCharHook(wxKeyEvent& event)
{
    int k = event.GetKeyCode () ;
    wxCommandEvent ev ;
    if ( k == WXK_ESCAPE ) EndModal ( wxID_CANCEL ) ; //OnCancel ( ev ) ;
    else if ( k == WXK_F1 ) myapp()->frame->OnHelp(ev) ;
    else event.Skip() ;
}

void TRestrictionEditor::res_ll_col ( wxListEvent &event )
    {
    int item = el->GetNextItem ( -1 , wxLIST_NEXT_ALL , wxLIST_STATE_SELECTED) ;
    if ( item != -1 )
        {
        pre = el->GetItemText(item) ;
        }

    int col = event.GetColumn() ;
    if ( col == global_sort_mode )
        {
        global_sort_ascending = !global_sort_ascending ;
        }
    else
        {
        global_sort_mode = col ;
        }
    pR_showGroupEnzymes ( gl->GetStringSelection() ) ;
    }

TRestrictionEditor::TRestrictionEditor(MyFrame * const parent, const wxString& title, const wxPoint& pos , const wxSize& size , const long style )
: wxDialog ( parent , -1 , title , pos , size , style )
    {
    myapp()->frame->push_help ( _T("GENtle:Restriction_Assistant") ) ;
    numberoffragments = 0 ;
    global_sort_mode = BY_NAME ;
    global_sort_ascending = true ;
    }

TRestrictionEditor::~TRestrictionEditor ()
    {
    myapp()->frame->pop_help () ;
    }

void TRestrictionEditor::initme ( /* not const*/ TVector * const _v )
    {
    _w = 600 ;
    _h = 550 ;
    v = _v ;
    SetTitle ( txt("res_editor") ) ;
    initRestrictionPage () ;
    }

void TRestrictionEditor::initRestrictionPage ()
    {
    int w = _w , h = _h ;
    int bo = 5 , th = 12 ;
    int rw = w , rh = h ;

    wxClientDC dc ( (wxWindow*) this ) ;
    dc.GetTextExtent ( _T("X") , &bo , &th ) ;
    th = th * 5 / 6 ;
    bo = th / 2 ;


    wxFlexGridSizer *v1 = new wxFlexGridSizer ( 2 , 1 , 5 , 5 ) ;
    wxBoxSizer *v2a = new wxBoxSizer ( wxVERTICAL ) ;
    wxFlexGridSizer *v2b = new wxFlexGridSizer ( 7 , 1 , 5 , 5 ) ;
    wxBoxSizer *v2c = new wxBoxSizer ( wxVERTICAL ) ;
    wxBoxSizer *h1 = new wxBoxSizer ( wxHORIZONTAL ) ;
    wxBoxSizer *h2 = new wxBoxSizer ( wxHORIZONTAL ) ;

    // Available enzymes list
    el = new wxListCtrl ( this , RSE_RES_LL , wxDefaultPosition , wxDefaultSize , wxLC_REPORT|wxLC_SINGLE_SEL|MYLISTBORDER ) ;
    el->InsertColumn ( 0 , txt("name") ) ;
    el->InsertColumn ( 1 , txt("cuts") ) ;
    v2a->Add ( new wxStaticText ( this , -1 , txt("t_available_enzymes") ) , 0 , wxEXPAND , 5 ) ;
    v2a->Add ( el , 1 , wxEXPAND , 5 ) ;

    // Cocktail
    el2 = new wxListCtrl ( this , RSE_COC_LL , wxPoint ( rw*2/3+bo , th*2 ) , wxSize ( rw/3-bo*2 , rh*2/3-th*2-bo ) , wxLC_REPORT|wxLC_SINGLE_SEL|MYLISTBORDER ) ;
    el2->InsertColumn ( 0 , txt("name") ) ;
    el2->InsertColumn ( 1 , txt("cuts") ) ;
    v2c->Add ( new wxStaticText ( this , -1 , txt("Cocktail") ) , 0 , wxEXPAND , 5 ) ;
    v2c->Add ( el2 , 1 , wxEXPAND , 5 ) ;

    wxFlexGridSizer *h1a = new wxFlexGridSizer ( 1 , 2 , 5 , 5 ) ;
    wxFlexGridSizer *h1d = new wxFlexGridSizer ( 1 , 2 , 5 , 5 ) ;
    wxBoxSizer *h1e = new wxBoxSizer ( wxHORIZONTAL ) ;
    wxBoxSizer *h1f = new wxBoxSizer ( wxHORIZONTAL ) ;

    // Enzyme groups
    wxString _gt = txt("group") ;
    gl = new wxChoice ( this , RSE_RES_DD ) ;
    gl->Append ( txt("Current") ) ;
    gl->Append ( txt("All") ) ;
    wxArrayString vs ;
    myapp()->frame->LS->getEnzymeGroups ( vs ) ;
    for ( int i = 0 ; i < vs.GetCount() ; i++ )
        {
        gl->Append ( vs[i] ) ;
        }
    gl->SetStringSelection ( txt("Current") ) ;
    h1a->Add ( new wxStaticText ( this , -1 , _gt ) , 1 , wxEXPAND , 5 ) ;
    h1a->Add ( gl , 1 , wxEXPAND , 5 ) ;

    wxString rbs[3] ;
    rbs[0] = txt("All enzymes") ;
    rbs[1] = txt("Enzymes that cut") ;
    rbs[2] = txt("Enzymes that don't cut") ;
    rb = new wxRadioBox ( this , RSE_RES_RB , txt("subsel") , wxDefaultPosition , wxDefaultSize , 3 , rbs , 1 , wxRA_SPECIFY_COLS ) ;

    nfst = new wxCheckBox ( this , RSE_COC_CB , txt("Do not create fragments below ") ) ;

    nfstv = new wxSpinCtrl ( this , -1 , _T("20") , wxDefaultPosition , wxSize ( MYSPINBOXSIZE , 30 ) ) ;
    nfst->SetValue ( true ) ;
    nfstv->SetSize ( 70 , th * 2 ) ;
    h1d->Add ( nfstv , 0 , wxALL|wxALIGN_CENTER_VERTICAL , 5 ) ;
    h1d->Add ( new wxStaticText ( this , -1 , txt("base pairs.") ) , 1 , wxEXPAND , 5 ) ;

    wxBoxSizer *v3a = new wxBoxSizer ( wxVERTICAL ) ;
    wxBoxSizer *v3b = new wxBoxSizer ( wxVERTICAL ) ;

    bb = new wxBitmapButton ( this , RSE_RES_AC ,
                              wxBitmap (myapp()->bmpdir.GetFullPath() + wxFileName::GetPathSeparator()+"/cocktail.bmp", wxBITMAP_TYPE_BMP),
                              wxDefaultPosition , wxDefaultSize , wxBU_AUTODRAW , wxDefaultValidator ,
                              txt("add2cocktail") ) ;
    wxBitmapButton *bib = new wxBitmapButton ( this , RSE_COC_CT ,
                              wxBitmap (myapp()->bmpdir.GetFullPath() + wxFileName::GetPathSeparator()+"/scissors.bmp", wxBITMAP_TYPE_BMP),
                              wxDefaultPosition , wxDefaultSize , wxBU_AUTODRAW , wxDefaultValidator ,
                              txt("start_res") ) ;

    v3a->Add ( bb , 1 , wxEXPAND , 5 ) ;
    v3a->Add ( new wxStaticText ( this , -1 , txt("add2cocktail") ) ) ;
    v3a->Add ( new wxStaticText ( this , -1 , _T("") ) , 0 , wxEXPAND , 5 ) ;
    v3a->Add ( bib , 1 , wxEXPAND , 5 ) ;
    v3a->Add ( new wxStaticText ( this , -1 , txt("start_res") ) ) ;

    btOK = new wxButton ( this , RSE_RES_OK , txt("b_done") ) ;
    btCC = new wxButton ( this , RSE_RES_CC , txt("b_cancel") ) ;
    createFragments = new wxCheckBox ( this , -1 , txt("t_create_fragments") ) ;
    add2gel = new wxCheckBox ( this , RSE_ADD2GEL , txt("t_add2gel") ) ;
    oneLaneEach = new wxCheckBox ( this , -1 , txt("t_one_lane_each") ) ;
    partialDigestion = new wxCheckBox ( this , RSE_PARTIAL_DIGESTION , txt("t_partial_digestion") ) ;
    v3b->Add ( new wxButton ( this , RSE_ADD_ALL , txt("t_add_all") ) , 0 , wxEXPAND , 5 ) ;
    v3b->Add ( new wxStaticText ( this , -1 , _T("") ) , 0 , wxEXPAND , 5 ) ;
    v3b->Add ( new wxButton ( this , RSE_COC_RM , txt("Remove enzyme") ) , 0 , wxEXPAND , 5 ) ;
    v3b->Add ( new wxStaticText ( this , -1 , _T("") ) , 0 , wxEXPAND , 5 ) ;
    v3b->Add ( btOK , 0 , wxEXPAND , 5 ) ;
    v3b->Add ( btCC , 0 , wxEXPAND , 5 ) ;
    v3b->Add ( new wxStaticText ( this , -1 , _T("") ) , 0 , wxEXPAND , 5 ) ;
    v3b->Add ( createFragments , 0 , wxEXPAND , 5 ) ;
    v3b->Add ( add2gel , 0 , wxEXPAND , 5 ) ;
    v3b->Add ( oneLaneEach , 0 , wxEXPAND , 5 ) ;
    v3b->Add ( partialDigestion , 0 , wxEXPAND , 5 ) ;
    createFragments->SetValue ( TRUE ) ;
    add2gel->SetValue ( FALSE ) ;
    oneLaneEach->SetValue ( FALSE ) ;
    partialDigestion->SetValue ( FALSE ) ;
    oneLaneEach->Disable() ;
    partialDigestion->Disable() ;

    h1f->Add ( v3a , 0 , wxEXPAND , 5 ) ;
    h1f->Add ( new wxStaticText ( this , -1 , _T("   ") ) , 0 , wxEXPAND , 5 ) ;
    h1f->Add ( v3b , 0 , wxEXPAND , 5 ) ;

    v2b->Add ( new wxStaticText ( this , -1 , _T("") ) , 0 , wxEXPAND , 5 ) ;
    v2b->Add ( h1a , 1 , wxEXPAND , 5 ) ;
    v2b->Add ( rb , 1 , wxEXPAND , 5 ) ;
    v2b->Add ( nfst , 1 , wxEXPAND , 5 ) ;
    v2b->Add ( h1d , 1 , wxEXPAND , 5 ) ;
    v2b->Add ( h1e , 1 , wxEXPAND , 5 ) ;
    v2b->Add ( h1f , 1 , wxEXPAND , 5 ) ;

    h1->Add ( v2a , 1 , wxEXPAND , 5 ) ;
    h1->Add ( v2b , 1 , wxEXPAND , 5 ) ;
    h1->Add ( v2c , 1 , wxEXPAND , 5 ) ;

    // "This enzyme cuts" list
    rsl = new wxListCtrl ( this , -1 , wxDefaultPosition , wxDefaultSize ,
                           wxLC_REPORT|wxLC_SINGLE_SEL|MYLISTBORDER ) ;
    rsl->InsertColumn ( 0 , _T("#") , wxLIST_FORMAT_LEFT , th*2 ) ;
    rsl->InsertColumn ( 1 , txt("from") ) ;
    rsl->InsertColumn ( 2 , txt("to") ) ;
    rsl->InsertColumn ( 3 , txt("length") ) ;

    // All fragments list
    rsl2 = new wxListCtrl ( this , -1 , wxDefaultPosition , wxDefaultSize ,
                            wxLC_REPORT|wxLC_SINGLE_SEL|MYLISTBORDER ) ;
    rsl2->InsertColumn ( 0 , _T("#") , wxLIST_FORMAT_LEFT , th*2 ) ;
    rsl2->InsertColumn ( 1 , txt("from") ) ;
    rsl2->InsertColumn ( 2 , txt("to") ) ;
    rsl2->InsertColumn ( 3 , txt("length") ) ;

    rsl->SetSize ( w/3 , h/3 ) ;

    h2->Add ( rsl , 1 , wxEXPAND , 5 ) ;
    h2->Add ( rsl2 , 1 , wxEXPAND , 5 ) ;

    v1->Add ( h1 , 1 , wxEXPAND , 5 ) ;
    v1->Add ( h2 , 1 , wxEXPAND , 5 ) ;

    SetSizer ( v1 ) ;
    v1->Fit ( this ) ;

    btOK->SetDefault() ;
    refreshCocktail () ;

    Centre() ;

    for ( int i = 0 ; i < v->rc.size() ; i++ )
        {
        cutcache.push_back ( TREcache ( v->rc[i].e->getName() , v->rc[i].getPos() ) ) ;
        }

    pR_showGroupEnzymes ( txt("Current") ) ;

    for ( int i = 0 ; i < myapp()->frame->lastCocktail.GetCount() ; i++ )
        {
        add2cocktail ( myapp()->frame->lastCocktail[i] ) ;
        }
    myapp()->frame->lastCocktail.Clear() ;
    }

void TRestrictionEditor::listEnzymesInGroup ( const wxString& gr , wxArrayString &vs ) const
    {
    //wxPrintf( "D: TRestrictionEditor::listEnzymesInGroup (gr: %s, vs.GetCount: %lu)\n" , gr, vs.GetCount() ) ;
    if ( gr == txt("Current") )
        {
        if (!v)
            {
            wxPrintf( "D: TRestrictionEditor::listEnzymesInGroup(%s) - v is NULL\n" , gr ) ;
            abort() ;
            }
        else
            {
            wxPrintf( "D: TRestrictionEditor::listEnzymesInGroup(%s) - (%s: %s) " , gr , v->getName() , v->getDescription() ) ;
            wxPrintf( " re:%lu" , v->re.GetCount() ) ;
            wxPrintf( ", re2:%lu\n" , v->re2.GetCount() ) ;
            }
        for ( unsigned int i = 0 ; i < v->re.GetCount() ; i++ )
            {
            if ( v->re[i]->getSequence().IsEmpty() )
                {
                wxPrintf( "D: TRestrictionEditor::listEnzymesInGroup(%s) - re[%u] (%s) has no sequence - abort\n" , gr , i , v->getName() ) ;
                abort() ;
                }
            vs.Add ( v->re[i]->getName() ) ;
            }
        for ( unsigned int i = 0 ; i < v->re2.GetCount() ; i++ )
            {
            if ( v->re2[i]->getSequence().IsEmpty() )
                {
                wxPrintf( "D: TRestrictionEditor::listEnzymesInGroup(%s) - re[%u] (%s) has no sequence - abort\n" , gr , i , v->getName() ) ;
                continue ;
                }
            vs.Add ( v->re2[i]->getName() ) ;
            }
        }
    else
        {
        myapp()->frame->LS->getEnzymesInGroup ( gr , vs ) ;
        }
    //wxPrintf( "D: TRestrictionEditor::listEnzymesInGroup (gr: %s, vs.GetCount: %lu)\n" , gr, vs.GetCount() ) ;
    }

void TRestrictionEditor::pR_showGroupEnzymes ( const wxString& gr )
    {
    //wxPrintf( "D: TRestrictionEditor::pR_showGroupEnzymes(%s) - start\n" , gr ) ;
    el->DeleteAllItems() ;
    rsl->DeleteAllItems() ;

    wxArrayString vs ;
    listEnzymesInGroup ( gr , vs ) ;

    // char t[100] ;
    int sel = rb->GetSelection() ;
    bool docut = true , donotcut = true ;
    if ( sel == 1 ) donotcut = false ;
    if ( sel == 2 ) docut = false ;
    int cnt = 0 ;
    // bool ascending = true ;
    vector <TREcache> list ;
    for ( int i = 0 ; i < vs.GetCount() ; i++ )
        {
        const int k = getcuts(vs[i]).GetCount() ;
        if ( ( k == 0 && donotcut ) || ( k > 0 && docut ) )
            {
            list.push_back ( TREcache ( vs[i] , k ) ) ;
            }
        }

    sort ( list.begin() , list.end() ) ;

    for ( int i = 0 ; i < list.size() ; i++ )
        {
        // sprintf ( t , "%d" , list[i].cut ) ;
        int z = el->InsertItem ( cnt , list[i].enzyme ) ;
        el->SetItem ( z , 1 , wxString::Format ( _T("%d") , list[i].cut ) ) ;
        cnt++ ;
        }

    if ( !pre.IsEmpty() )
        {
        int pre_id = el->FindItem ( 0 , pre ) ;
        el->SetItemState ( pre_id , wxLIST_STATE_SELECTED , wxLIST_STATE_SELECTED );
        el->EnsureVisible ( pre_id ) ;
        el->SetFocus() ;
        pre = _T("") ;
        }
    //wxPrintf( "D: TRestrictionEditor::pR_showGroupEnzymes(%s) - end\n" , gr ) ;
    }

wxArrayInt TRestrictionEditor::getcuts ( const wxString& enzyme )
    {
    //wxPrintf ( "D: TRestrictionEditor::getcuts(%s) - start\n" , enzyme ) ;
    wxArrayInt ret ;
    { // block constraining use of variable j
    int j ;
    for ( j = 0 ; j < nocut.GetCount() && nocut[j] != enzyme ; j++ ) ;
    if ( j < nocut.GetCount() )
        {
        //wxPrintf ( "D: TRestrictionEditor::getcuts(%s) - ret empty since known nocut\n" , enzyme ) ;
        return ret ; // No cuts, returning empty list
        }
    }

    // Looking for cuts in cache
    for ( int j = 0 ; j < cutcache.size() ; j++ )
        {
        if ( cutcache[j].enzyme == enzyme )
            {
            ret.Add ( cutcache[j].cut ) ;
            }
        }

    if ( ret.GetCount() == 0 ) // Determine cuts
        {
        TRestrictionEnzyme *e = myapp()->frame->LS->getRestrictionEnzyme ( enzyme );
        if ( e->getSequence().length() < 3 )
            {
            //wxPrintf ( "D: TRestrictionEditor::getcuts(%s) - ret invalid sequence '%s' -> notcut\n" , enzyme , e->getSequence() ) ;
            return ret ;
            }
        else
            {
            vector <TRestrictionCut> x ;
            v->getCuts ( e , x ) ;
            if ( x.size() == 0 ) // No cuts
                {
                nocut.Add ( enzyme ) ;
                //wxPrintf ( "D: TRestrictionEditor::getcuts(%s) - ret notcut\n" , enzyme ) ;
                return ret ;
                }
            for ( int k = 0 ; k < x.size() ; k++ )
                {
                //wxPrintf ( "D: TRestrictionEditor::getcuts(%s) - adds cut at %d\n" , enzyme , x[k].getPos() ) ;
                cutcache.push_back ( TREcache ( enzyme , x[k].getPos() ) ) ;
                ret.Add ( x[k].getPos() ) ;
                }
            }
        }
    //wxPrintf( "D: TRestrictionEditor::getcuts(%s) - end - Found %lu cuts\n" , enzyme , ret.GetCount()) ;
    return ret ;
    }

void TRestrictionEditor::res_ll_act ( wxListEvent &event )
    {
    int i = event.GetIndex () ;
    wxString s = el->GetItemText ( i ) ;
    TRestrictionEnzyme *e = myapp()->frame->LS->getRestrictionEnzyme ( s ) ;
    TEnzymeDialog ed ( this , s , wxPoint(-1,-1) , wxSize(600,400) , wxDEFAULT_DIALOG_STYLE|wxCENTRE ) ;
    ed.initme ( e , true ) ;
    ed.ShowModal() ;
    }

void TRestrictionEditor::res_ll ( wxListEvent &event )
    {
    int i = event.GetIndex () ;
    last_enzyme_selected = el->GetItemText ( i ) ;
    pR_showFragments ( i ) ;
    }

void TRestrictionEditor::pR_showFragments ( int i )
    {
    //wxPrintf( "D: TRestrictionEditor::pR_showFragments(%d) - start\n" , i ) ;
    wxString enzyme = el->GetItemText ( i ) ;
    wxArrayInt vi = getcuts ( enzyme ) ;
    listFragments ( rsl , vi ) ;
    //wxPrintf( "D: TRestrictionEditor::pR_showFragments(%d) - end\n" , i ) ;
    }

void TRestrictionEditor::getFragmentList ( wxArrayInt& cuts , vector <TFragment>& fragments , const bool clear ) const
    {
    //wxPrintf( "D: TRestrictionEditor::getFragmentList - start - cuts.GetCount=%d\n" , cuts.GetCount() ) ;

    if (!v)
        {
        wxPrintf( "D: TRestrictionEditor::getFragmentList - v is NULL\n" ) ;
        abort() ;
        }

    if ( clear ) fragments.clear () ;
    if ( cuts.GetCount() == 0 ) return ;
    if ( !v->isCircular() ) // Adding last fragment for linear DNA
        {
        int from = cuts.Last() ;
        int to = v->getSequenceLength() ;
        if ( from != to )
            cuts.Add ( to ) ;
        }

    for ( int i = 0 ; i < cuts.GetCount() ; i++ )
        {
        TFragment fr ;

        int from = 0 ;
        if ( i > 0 ) from = cuts[i-1] ;
        if ( i == 0 && v->isCircular() ) from = cuts.Last() ;
        fr.from = from ;
        fr.to = cuts[i] ;

        int len = cuts[i] - from ;
        if ( len <= 0 ) len += v->getSequenceLength() ;
        fr.length = len ;
        fragments.push_back ( fr ) ;
        }
    //wxPrintf( "D: TRestrictionEditor::getFragmentList - end\n" ) ;
    }

void TRestrictionEditor::listFragments ( wxListCtrl * const list , wxArrayInt &vi ) /* not const */
    {
    //wxPrintf( "D: TRestrictionEditor::listFragments - start\n" ) ;
    if (!v)
        {
        wxPrintf( "E: TRestrictionEditor::listFragments - v is NULL\n" ) ;
        abort() ;
        }
    if (!list)
        {
        wxPrintf( "E: TRestrictionEditor::listFragments - list is NULL\n" ) ;
        abort() ;
        }

    list->DeleteAllItems() ;
    if ( vi.GetCount() == 0 )
        {
        wxPrintf( "D: TRestrictionEditor::listFragments - ret vi.GetCount() == 0\n" ) ;
        return ;
        }

    if ( !v->isCircular() ) // Adding last fragment for linear DNA
        {
        int from = vi.Last() ;
        int to = v->getSequenceLength() ;
        if ( from != to ) vi.Add ( to ) ;
        }
    numberoffragments = vi.GetCount() ; // not const

    // char u[100] ;
    for ( int i = 0 ; i < vi.GetCount() ; i++ )
        {
        //sprintf ( u , "%d" , i+1 ) ; // Number
        int k = list->InsertItem ( i , wxString::Format ( _T("%d") , i+1 ) ) ;

        int from = 0 ;
        if ( i > 0 ) from = vi[i-1] ;
        if ( i == 0 && v->isCircular() ) from = vi.Last() ;
        //sprintf ( u , "%d" , from ) ; // From
        list->SetItem ( k , 1 , wxString::Format ( _T("%d") , from ) ) ;

        //sprintf ( u , "%d" , vi[i] ) ; // To
        list->SetItem ( k , 2 , wxString::Format ( _T("%d") , vi[i] ) ) ;

        int len = vi[i] - from ;
        if ( len <= 0 ) len += v->getSequenceLength() ;
        //sprintf ( u , "%d" , len ) ; // Length
        list->SetItem ( k , 3 , wxString::Format ( _T("%d") , len ) ) ;
        }
    //wxPrintf( "D: TRestrictionEditor::listFragments - end\n" ) ;
    }

void TRestrictionEditor::refreshCocktail () /* not const */
    {
    //wxPrintf( "D: TRestrictionEditor::refreshCocktail - start\n" ) ;
    wxArrayString vs = cocktail ;
    el2->DeleteAllItems() ;
    // char t[100] ;
    for ( int i = 0 ; i < vs.GetCount() ; i++ )
        {
        int k = getcuts(vs[i]).GetCount() ;
        //sprintf ( t , "%d" , k ) ;
        int z = el2->InsertItem ( i , vs[i] ) ;
        el2->SetItem ( z , 1 , wxString::Format ( _T("%d") , k ) ) ;
        }

    wxArrayInt vi , vit ;
    for ( int i = 0 ; i < cocktail.GetCount() ; i++ )
        {
        vit = getcuts ( cocktail[i] ) ;
        for ( int j = 0 ; j < vit.GetCount() ; j++ )
                        vi.Add ( vit[j] ) ;
        }
    vi.Sort ( cmpint ) ;
    vit = vi ;
    vi.Clear () ;
    for ( int i = 0 ; i < vit.GetCount() ; i++ )
        {
        if ( vi.GetCount() == 0 || vi.Last() != vit[i] )
            {
            vi.Add ( vit[i] ) ;
            }
        }
    cocktailFragments = vi ; // not const
    listFragments ( rsl2 , vi ) ;
    //wxPrintf( "D: TRestrictionEditor::refreshCocktail - end\n" ) ;
    }

void TRestrictionEditor::add2cocktail ( const wxString& s ) /* not const */
    {
    //wxPrintf( "D: TRestrictionEditor::add2cocktail(%s) - start\n" , s ) ;
    if ( s.IsEmpty() )
        {
        wxPrintf( "D: TRestrictionEditor::add2cocktail(%s) - ret s empty\n" , s ) ;
        return ;
        }
    int i = 0 ;
    while ( i < cocktail.GetCount() && cocktail[i] != s )
        {
        i++ ;
        }
    if ( i < cocktail.GetCount() )
        {
        wxPrintf( "D: TRestrictionEditor::add2cocktail(%s) - enzyme already added to cocktail\n" , s ) ;
        return ; // Already in there
        }
    cocktail.Add ( s ) ;
    cocktail.Sort() ;
    refreshCocktail () ;
    //wxPrintf( "D: TRestrictionEditor::add2cocktail(%s) - end\n" , s ) ;
    }

void TRestrictionEditor::del_from_cocktail ( const wxString& s ) /* not const */
    {
    //wxPrintf( "W: TRestrictionEditor::del_from_cocktail(%s) - start\n" , s ) ;
    int i = 0 ;
    while ( i < cocktail.GetCount() && cocktail[i] != s )
        {
        i++ ;
        }
    if ( i == cocktail.GetCount() )
        {
        wxPrintf( "W: TRestrictionEditor::del_from_cocktail(%s) - ret enzyme not in cocktail\n" , s ) ;
        return ; // Not there
        }
    cocktail.RemoveAt ( i ) ;
    refreshCocktail () ;
    //wxPrintf( "W: TRestrictionEditor::del_from_cocktail(%s) - end\n" , s ) ;
    }

// "Restriction" handlers

void TRestrictionEditor::res_dd ( wxCommandEvent &event )
    { // This handles the enzyme groups dropdown list
    wxString s = event.GetString() ;
    pR_showGroupEnzymes ( s ) ;
    }

void TRestrictionEditor::res_cb ( wxCommandEvent &event )
    {
    wxString s = gl->GetStringSelection() ;
    pR_showGroupEnzymes ( s ) ;
    }

void TRestrictionEditor::res_ac ( wxCommandEvent &event )
    { // This handles the "Add to cocktail" button
    add2cocktail ( last_enzyme_selected ) ;
    }

void TRestrictionEditor::res_coc_rm ( wxCommandEvent &event )
    { // This handles the "Remove from cocktail" button
    del_from_cocktail ( last_enzyme_selected_coc ) ;
    }

void TRestrictionEditor::res_coc_ll ( wxListEvent &event )
    {
    int i = event.GetIndex () ;
    last_enzyme_selected_coc = el2->GetItemText ( i ) ;
    }

void TRestrictionEditor::onOK ( wxCommandEvent &event )
    {
    EndModal ( wxID_OK ) ;
    }

void TRestrictionEditor::onCancel ( wxCommandEvent &event )
    {
    EndModal ( wxID_CANCEL ) ;
    }

// "Cocktail" handlers

void TRestrictionEditor::res_ct ( wxCommandEvent &event )
    { // This handles the "Start restriction" button
    if ( numberoffragments == 0 )
        {
        event.Skip() ;
        return ;
        }

    myass ( nfst , "TRestrictionEditor::res_ct:1" ) ;
    myass ( v , "TRestrictionEditor::res_ct:2" ) ;
    myass ( remoteCocktail , "TRestrictionEditor::res_ct:3" ) ;

    if ( nfst->GetValue() ) v->setAction ( _T("RESTRICTION") , nfstv->GetValue() ) ;
    else v->setAction ( _T("RESTRICTION") , 0 ) ;

    EndModal ( wxID_OK ) ;
    }

void TRestrictionEditor::res_checkbox ( wxCommandEvent &event )
    {
    nfstv->Enable ( nfst->GetValue() ) ;
    }

void TRestrictionEditor::res_cb_add2gel ( wxCommandEvent &event )
    {
    oneLaneEach->Enable ( add2gel->GetValue() && !partialDigestion->GetValue() ) ;
    if ( partialDigestion->GetValue() ) oneLaneEach->SetValue ( FALSE ) ;
    partialDigestion->Enable ( add2gel->GetValue() ) ;
    }

void TRestrictionEditor::onAddAll ( wxCommandEvent &event )
    {
    wxArrayString vs ;
    wxString s = gl->GetStringSelection() ;
    listEnzymesInGroup ( s , vs ) ;
    for ( int a = 0 ; a < vs.GetCount() ; a++ )
        {
        add2cocktail ( vs[a] ) ;
        }
    }

void TRestrictionEditor::iterateFragments ( const wxArrayInt& cuts , vector <TFragment> &fragments , const int depth ) const
    {
    //wxPrintf( "D: TRestrictionEditor::iterateFragments - start\n" ) ;
    if ( cuts.size() == 0 ) return ;
    wxArrayInt cuts2 = cuts ;
    getFragmentList ( cuts2 , fragments , false ) ;
    for ( int a = 0 ; a < cuts.size() ; a++ )
        {
        cuts2 = cuts ;
        cuts2.RemoveAt ( a ) ;
        iterateFragments ( cuts2 , fragments , depth + 1 ) ;
        }
    if ( depth > 0 ) return ;

    // Clean up double fragments
    int gsm = global_sort_mode ;
    global_sort_mode = BY_CUTS ;
    sort ( fragments.begin() , fragments.end() ) ;

    for ( int a = 1 ; a < fragments.size() ; a++ )
        {
        if ( ! ( fragments[a-1] == fragments[a] ) )
            {
            continue ;
            }
        for ( int b = a ; b + 1 < fragments.size() ; b++ )
            {
            fragments[b] = fragments[b+1] ;
            }
        fragments.pop_back() ;
        a-- ;
        }

    global_sort_mode = gsm ;
    //wxPrintf( "D: TRestrictionEditor::iterateFragments - end\n" ) ;
    }
