
struct Selection
{
    Node *n;    // selected node, or selection is inside of this node if width!=1
    int start;  // from 0 to numchildren inclusive
    int width;  // idem, - start

    int subtree;

    Selection(Node *_n = NULL, int _s = 0, int _w = 1) : n(_n), start(_s), width(_w), subtree(-1) {}

    bool operator==(const Selection &s) { return n==s.n && start==s.start && width==s.width; }

    int NumNodes() { return width; }
    bool Thin() { return width==0; }

    Node *GetNode(int i = 0) { return i>=width || !n ? NULL : width==1 ? n : n->GetChild(start+i); }

    Unparsed *GetEditBox() { return n && n->type==T_UNPARSED ? n->up : NULL; }

    void Merge(Selection &a, Selection &b)
    {
        if(a.n==b.n)                    // drag within a tree, usually thin selections
        {
            *this = a;
            start = min(a.start, b.start);
            width = max(a.start+a.width, b.start+b.width)-start;
        }
        else
        {
            int ad = a.n->Depth();
            int bd = b.n->Depth();
            int d = min(ad, bd);
            Node *ap = a.n->Parent(ad-d);
            Node *bp = b.n->Parent(bd-d);
            if(ap==bp)                  // drag between subtree and its parent
            {
                n = ap;
                start = 0;
                width = 1;
                return;
            }
            while(ap->parent)           // drag between two unrelated subtrees
            {
                if(ap->parent==bp->parent)
                {
                    n = ap->parent;
                    int apos = n->FindPos(ap);
                    int bpos = n->FindPos(bp);
                    start = min(apos, bpos);
                    width = max(apos, bpos)+1-start;
                    return;
                }
                ap = ap->parent;
                bp = bp->parent;
            }
            *this = Selection();        // drag between different functions
        }
    }

    void Draw(wxDC &dc, bool hover, bool cursoronly = false)
    {
        if(!n) return;

        DisplayedSubtree &dst = sys->GetSubTree(subtree);
        int x = dst.x+n->GetX();
        int y = dst.y+n->GetY();
        int xs = n->xs;
        int ys = n->ys;

        #ifndef SIMPLERENDER
            dc.SetLogicalFunction(wxXOR);
        #endif

        if(!cursoronly)
        {
            uint col = hover ? 0x202020 : 0xFFFFFF;

            if(width==1)
            {
            }
            else if(width==0)
            {
                xs = 4;

                if(start<n->NumChildren())
                {
                    Node *a = n->GetChild(start);
                    x += a->ox-(start ? sys->bf_comma.xs-8 : xs+4);
                }
                else if(n->NumChildren())
                {
                    x += n->xs-sys->bf_closepar.xs+4;
                }
                else
                {
                    x += n->id->xs;
                    x += (sys->bf_openpar.xs+sys->bf_closepar.xs-xs)/2;
                }
            }
            else
            {
                Node *a = GetNode(0);
                Node *b = GetNode(width-1);

                x += a->ox;
                y += a->oy;

                if(n->vertlayout)
                {
                    ys = b->oy-a->oy+b->ys;
                    xs = 0;
                    loop(i, width) xs = max(xs, GetNode(i)->xs);
                }
                else
                {
                    xs = b->ox-a->ox+b->xs;
                    ys = 0;
                    loop(i, width) ys = max(ys, GetNode(i)->ys);
                }
            }

            DrawRectangle(dc, col, x, y, xs, ys, n->type==T_UNPARSED && !hover);
            if(n->type==T_UNPARSED && !hover) DrawRectangle(dc, col, x+1, y+1, xs-2, ys-2, false);
        }

        if(!hover) if(Unparsed *up = GetEditBox())
            #ifdef SIMPLERENDER
            up->DrawCursor(dc, 0x00FF00, cursoronly, x, y);
            #else
            up->DrawCursor(dc, 0xFFFF, cursoronly, x, y);
            #endif

        #ifndef SIMPLERENDER
            dc.SetLogicalFunction(wxCOPY);
        #endif
    }

    void DelOp(bool isdel)
    {
        if(Thin())
        {
            if(isdel) { if(start<n->NumChildren()) delete n->RemoveNthChild(start);   }
            else      { if(start)                  delete n->RemoveNthChild(--start); }

        }
        else if(width>1)
        {
            loop(i, width)
            {
                delete n->RemoveNthChild(start);
            }
            width = 0;
        }
        else
        {
            if(n->parent)
            {
                start = n->parent->FindPos(n);
                n = n->parent;
                delete n->RemoveNthChild(start);
                width = 0;
            }
            else if(n->fparent)
            {
                // FIXME: replace body by 0? or perform delete on all function call parents?
            }
        };
    }

    bool Normalize()
    {
        if(width==1)
        {
            if(n->parent==NULL) return false;
            start = n->parent->FindPos(n);
            n = n->parent;
        }
        return true;
    }

    void DeNormalize()
    {
        if(width==1)
        {
            n = n->GetChild(start);
            start = 0;
        }
    }

    char *Dir(bool ctrl, bool shift, wxDC &dc, int dx, int dy)
    {
        if(!Normalize()) return NULL;

        if(shift)
        {
            if(dx<0 && start)
            {
                start--;
                width++;
            }
            else if(dx>0 && start+width<n->NumChildren())
            {
                width++;
            }
        }
        else
        {
            if(dx)
            {
                if(width)
                {
                    if(dx>0) start += width;
                    width = 0;
                }
                else
                {
                    if(dx<0)
                    {
                        if(start) start--;
                        else
                        {
                            width = 1;
                            Normalize();
                            width = 0;
                        }
                    }
                    else if(dx>0)
                    {
                        if(start<n->NumChildren()) start++;
                        else
                        {
                            width = 1;
                            if(Normalize()) start++;
                            width = 0;
                        }
                    }
                }
            }
        }

        DeNormalize();

        sys->Refresh(); //
        return NULL;
    }

    char *Cursor(int k, bool ctrl, bool shift, wxDC &dc)
    {
        switch(k)
        {
            case A_UP:    return Dir(ctrl, shift, dc,  0, -1);
            case A_DOWN:  return Dir(ctrl, shift, dc,  0,  1);
            case A_LEFT:  return Dir(ctrl, shift, dc, -1,  0);
            case A_RIGHT: return Dir(ctrl, shift, dc,  1,  0);
        }
        return NULL;
    }

};

