/*
 LICENSE:
 
 This program is free software; you can redistribute it and/or modify  
 it under the terms of the GNU General Public License as published by  
 the Free Software Foundation; either version 2 of the License, or     
 (at your option) any later version.                                   
 
 Copyright (C) 2004, GanttProject Development Team
 */
package net.sourceforge.ganttproject.time;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import net.sourceforge.ganttproject.time.gregorian.FramerImpl;

/**
 * Created by IntelliJ IDEA.
 * 
 * @author bard
 */
public class TimeUnitFunctionOfDateImpl extends TimeUnitImpl
        implements TimeUnitFunctionOfDate {
    private final TimeUnit myDirectAtomUnit;
    private int myAtomCount = -1;
    public TimeUnitFunctionOfDateImpl(String name, TimeUnitGraph graph,
            TimeUnit directAtomUnit, DateFrameable framer) {
        super(name, graph, directAtomUnit, framer);
        myDirectAtomUnit = directAtomUnit;
    }

    public TimeUnit createTimeUnit(Date date) {
        return new ParameterizedTimeUnitImpl(date);
    }

    public int getAtomCount(TimeUnit atomUnit) {
        throw new UnsupportedOperationException(
                "This time unit ("+toString()+") is function of date. Use method createTimeUnit() to create a parameterized instance.");
    }

    private class ParameterizedTimeUnitImpl implements TimeUnit {
        private final Date myRightDate;

        private final Date myLeftDate;

        //private int myAtomCount = -1;

        public ParameterizedTimeUnitImpl(Date myBaseDate) {
            this.myRightDate = TimeUnitFunctionOfDateImpl.this
                    .adjustRight(myBaseDate);
            this.myLeftDate = TimeUnitFunctionOfDateImpl.this
                    .adjustLeft(myBaseDate);
        }

        public String getName() {
            return TimeUnitFunctionOfDateImpl.this.getName();
        }

        public boolean isConstructedFrom(TimeUnit unit) {
            return TimeUnitFunctionOfDateImpl.this.isConstructedFrom(unit);
        }
/*
        public int getAtomCount(TimeUnit atomUnit) {
            Integer result = myUnit_Count.get(atomUnit);
            if (result==null) {
                result = _getAtomCount(atomUnit);
                myUnit_Count.put(atomUnit, result);
            }
            return result;
        }
        private int _getAtomCount(TimeUnit atomUnit) {
            if (atomUnit == TimeUnitFunctionOfDateImpl.this || atomUnit == this) {
                return 1;
            }
            int atomCount = 0;
            final TimeUnit directAtomUnit = getDirectAtomUnit(); 
            for (Date leftDate = TimeUnitFunctionOfDateImpl.this.myDirectFrameable
                    .jumpLeft(myRightDate); leftDate.compareTo(myLeftDate) >= 0;) {

                leftDate = TimeUnitFunctionOfDateImpl.this.myDirectFrameable
                        .jumpLeft(leftDate);
                if (!atomUnit.equals(directAtomUnit)) {
                    TimeUnit tmp = directAtomUnit instanceof TimeUnitFunctionOfDate ? ((TimeUnitFunctionOfDate)directAtomUnit).createTimeUnit(leftDate) : directAtomUnit;
                    atomCount += tmp.getAtomCount(atomUnit);
                }
                else {
                    atomCount++;
                }
            }
            return atomCount;
//            int directAtomCount = getDirectAtomCount();
//            return directAtomCount * getDirectAtomUnit().getAtomCount(atomUnit);
        }
*/
        public int getAtomCount(TimeUnit atomUnit) {
            if (atomUnit == TimeUnitFunctionOfDateImpl.this || atomUnit == this) {
                return 1;
            }
            int directAtomCount = getDirectAtomCount();
            return directAtomCount * getDirectAtomUnit().getAtomCount(atomUnit);
        }

        private int getDirectAtomCount() {
            if (myAtomCount == -1) {
                myAtomCount = 0;
                for (Date leftDate = TimeUnitFunctionOfDateImpl.this.myDirectAtomUnit
                        .jumpLeft(myRightDate); leftDate.compareTo(myLeftDate) >= 0; myAtomCount++) {

                    leftDate = TimeUnitFunctionOfDateImpl.this.myDirectAtomUnit
                            .jumpLeft(leftDate);
                }
            }
            return myAtomCount;
        }
        
        public TimeUnit getDirectAtomUnit() {
            return TimeUnitFunctionOfDateImpl.this.getDirectAtomUnit();
        }

        public Date adjustRight(Date baseDate) {
            return TimeUnitFunctionOfDateImpl.this.adjustRight(baseDate);
        }

        public Date adjustLeft(Date baseDate) {
            return TimeUnitFunctionOfDateImpl.this.adjustLeft(baseDate);
        }

        public Date jumpLeft(Date baseDate) {
            return TimeUnitFunctionOfDateImpl.this.jumpLeft(baseDate);
        }

        public Date shiftDate(Date base, long length) {
            return TimeUnitFunctionOfDateImpl.this.shiftDate(base, length);
        }
        
        public boolean isWorkingInterval(Date intervalStart) {
            return TimeUnitFunctionOfDateImpl.this.isWorkingInterval(intervalStart);
        }
        public void setTextFormatter(TextFormatter formatter) {
            TimeUnitFunctionOfDateImpl.this.setTextFormatter(formatter);
        }

        public TimeUnitText format(Date baseDate) {
            TextFormatter formatter = TimeUnitFunctionOfDateImpl.this
                    .getTextFormatter();
            return formatter == null ? new TimeUnitText("") : formatter.format(
                    this, baseDate);
        }

        public boolean equals(Object o) {
            return TimeUnitFunctionOfDateImpl.this.equals(o);
        }

        @Override
        public String toString() {
            return getName()+" started at "+myLeftDate;
        }        
        
    }
}
