/*
 *******************************************************************************
 * Copyright (C) 2002-2012, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 */
package org.unicode.cldr.util;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

import com.ibm.icu.text.UnicodeSet;
import com.ibm.icu.text.UnicodeSetIterator;

public abstract class Visitor {
    
    public void doAt(Object item) {
        if (item instanceof Collection) {
            doAt((Collection) item);
        } else if (item instanceof Map) {
            doAt((Map) item);
        } else if (item instanceof Object[]) {
            doAt((Object[]) item);
        } else if (item instanceof UnicodeSet) {
            doAt((UnicodeSet) item);
        } else {
            doSimpleAt(item);
        }
    }

    public int count(Object item) {
        if (item instanceof Collection) {
            return ((Collection) item).size();
        } else if (item instanceof Map) {
            return ((Map) item).size();
        } else if (item instanceof Object[]) {
            return ((Object[]) item).length;
        } else if (item instanceof UnicodeSet) {
            return ((UnicodeSet) item).size();
        } else {
            return 1;
        }
    }

    // the default implementation boxing
    
    public void doAt(int o) {
        doSimpleAt(new Integer(o));
    }
    public void doAt(double o) {
        doSimpleAt(new Double(o));
    }
    public void doAt(char o) {
        doSimpleAt(new Character(o));
    }
    
    // for subclassing
    
    protected void doAt (Collection c) {
        if (c.size() == 0) doBefore(c, null);
        Iterator it = c.iterator();
        boolean first = true;
        Object last = null;
        while (it.hasNext()) {
            Object item = it.next();
            if (first) {
                doBefore(c, item);
                first = false;
            } else {
                doBetween(c, last, item);
            }    
            doAt(last=item);
        }
        doAfter(c, last);
    }

    protected void doAt (Map c) {
        doAt(c.entrySet());
    }

    protected void doAt (UnicodeSet c) {
        if (c.size() == 0) doBefore(c, null);
        UnicodeSetIterator it = new UnicodeSetIterator(c);
        boolean first = true;
        Object last = null;
        Object item;
        CodePointRange cpr0 = new CodePointRange();
        CodePointRange cpr1 = new CodePointRange();
        CodePointRange cpr;
        
        while(it.nextRange()) {
            if (it.codepoint == UnicodeSetIterator.IS_STRING) {
                item = it.string;
            } else {
                cpr = last == cpr0 ? cpr1 : cpr0;   // make sure we don't override last
                cpr.codepoint = it.codepoint;
                cpr.codepointEnd = it.codepointEnd;
                item = cpr;
            }           
            if (!first) {
                doBefore(c, item);
                first = true;
            } else {
                doBetween(c, last, item);
            }
            doAt(last = item);
        }
        doAfter(c, last);
    }
    
    protected void doAt (Object[] c) {
        doBefore(c, c.length == 0 ? null : c[0]);
        Object last = null;
        for (int i = 0; i < c.length; ++i) {
            if (i != 0) doBetween(c, last, c[i]);
            doAt(last = c[i]);
        }
        doAfter(c, last);
    }
    
    public static class CodePointRange{
        public int codepoint, codepointEnd;
        @Override
        public String toString() {
            return "[" + codepoint + ", " + codepointEnd + "]";
        }
    }
    
    // ===== MUST BE OVERRIDEN =====
    
    abstract protected void doBefore(Object container, Object item);
    abstract protected void doBetween(Object container, Object lastItem, Object nextItem);
    abstract protected void doAfter(Object container, Object item);   
    abstract protected void doSimpleAt(Object o);
    
}