package net.sourceforge.ganttproject.chart;

import java.awt.Dimension;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import net.sourceforge.ganttproject.calendar.GPCalendar;
import net.sourceforge.ganttproject.calendar.GPCalendar.DayType;
import net.sourceforge.ganttproject.chart.GraphicPrimitiveContainer.Rectangle;
import net.sourceforge.ganttproject.gui.UIConfiguration;
import net.sourceforge.ganttproject.gui.options.model.GPOptionChangeListener;
import net.sourceforge.ganttproject.gui.options.model.GPOptionGroup;
import net.sourceforge.ganttproject.gui.zoom.ZoomEvent;
import net.sourceforge.ganttproject.task.TaskLength;
import net.sourceforge.ganttproject.task.TaskManager;
import net.sourceforge.ganttproject.time.TimeFrame;
import net.sourceforge.ganttproject.time.TimeUnit;
import net.sourceforge.ganttproject.time.TimeUnitFunctionOfDate;
import net.sourceforge.ganttproject.time.TimeUnitStack;

public class ChartModelBase implements ChartViewState.Listener {

    public static final Object STATIC_MUTEX = new Object();

    private final OptionEventDispatcher myOptionEventDispatcher = new OptionEventDispatcher();
    
    private final ChartHeaderImpl myChartHeader;

    private final ChartGridImpl myChartGrid;

    private Dimension myBounds;

    private Date myStartDate;

    protected int myAtomUnitPixels;

    private TimeFrame[] myTimeFrames;

    protected final TimeUnitStack myTimeUnitStack;

    private TimeUnit myTopUnit;

    protected TimeUnit myBottomUnit;

    protected java.util.List myTimeUnitVisitors = new ArrayList();

    protected final BottomUnitLineRendererImpl myBottomUnitLineRenderer;

    protected TimeFrameWidthFunction myFrameWidthFunction;

    private RegularFramesWithFunction myRegularFrameWidthFunction = new RegularFramesWithFunction();

    private SkewedFramesWidthFunction mySkewedFrameWidthFunction = new SkewedFramesWidthFunction();

    private final BackgroundRendererImpl myBackgroundRenderer;

    private final StyledPainterImpl myPainter;

    private final List myOptionListeners = new ArrayList();
    
    private final UIConfiguration myProjectConfig;

	private CachingOffsetCalculatorImpl myCachingOffsetCalculator;

    public ChartModelBase(TaskManager taskManager, TimeUnitStack timeUnitStack,
            UIConfiguration projectConfig) {
        myTaskManager = taskManager;
        myProjectConfig = projectConfig;
        myChartUIConfiguration = new ChartUIConfiguration(projectConfig);
        myPainter = new StyledPainterImpl(myChartUIConfiguration);
        myTimeUnitStack = timeUnitStack;
        myChartHeader = new ChartHeaderImpl(this);
        myChartGrid = new ChartGridImpl(this, projectConfig);
        myBottomUnitLineRenderer = new BottomUnitLineRendererImpl(this);
        myBackgroundRenderer = new BackgroundRendererImpl(this);
        myTimeUnitVisitors.add(myChartHeader);
        myTimeUnitVisitors.add(myChartGrid);
        myTimeUnitVisitors.add(myBottomUnitLineRenderer);
        myCachingOffsetCalculator = new CachingOffsetCalculatorImpl(myTimeUnitStack);
    }

    public void paint(Graphics g) {
        int height = (int) getBounds().getHeight()
                - getChartUIConfiguration().getHeaderHeight();
        myChartGrid.setHeight(height);
        myBackgroundRenderer.setHeight(height);
        if (getTopUnit().isConstructedFrom(myBottomUnit)) {
            myFrameWidthFunction = myRegularFrameWidthFunction;
            for (int i = 0; i < myTimeUnitVisitors.size(); i++) {
                ((TimeUnitVisitor) myTimeUnitVisitors.get(i)).setEnabled(true);
            }
            paintRegularTimeFrames(g, getTimeFrames(null));
        } else {
            myFrameWidthFunction = mySkewedFrameWidthFunction;
            mySkewedFrameWidthFunction.initialize();
            paintSkewedTimeFrames(g);
        }
    }

    protected void enableRenderers1() {
        myChartHeader.setEnabled(false);
        myBottomUnitLineRenderer.setEnabled(true);
        myChartGrid.setEnabled(true);
    }

    protected void enableRenderers2() {
        myChartHeader.setEnabled(true);
        myBottomUnitLineRenderer.setEnabled(false);
        myChartGrid.setEnabled(false);
    }

    private void paintSkewedTimeFrames(Graphics g) {
        TimeUnit savedBottomUnit = myBottomUnit;
        TimeUnit topUnit = getTopUnit();
        setTopUnit(myBottomUnit);
        myTimeFrames = null;
        enableRenderers1();
        TimeFrame[] timeFrames = getTimeFrames(null);
        paintRegularTimeFrames(g, timeFrames);
        Date exactStart = timeFrames[0].getStartDate();
        // System.err.println("... done");
        // System.err.println("[ChartModelImpl] rendering skewed frames. Top
        // unit="+myTopUnit+" bottom unit="+myBottomUnit);
        // System.err.println(" rendering top line");
        myTimeFrames = null;
        setTopUnit(topUnit);
        myBottomUnit = topUnit;
        enableRenderers2();
        timeFrames = getTimeFrames(exactStart);

        paintRegularTimeFrames(g, timeFrames);
        myBottomUnit = savedBottomUnit;
        //
        // System.err.println(" rendering bottom line");
    }

    protected void paintMainArea(Graphics mainArea, Painter p) {
        myChartGrid.getPrimitiveContainer().paint(p, mainArea);
    }

    private void paintRegularTimeFrames(Graphics g, TimeFrame[] timeFrames) {
        fireBeforeProcessingTimeFrames();
        for (int i = 0; i < timeFrames.length; i++) {
            TimeFrame next = timeFrames[i];
            fireFrameStarted(next);
            TimeUnit topUnit = next.getTopUnit();
            fireUnitLineStarted(topUnit);
            fireUnitLineFinished(topUnit);
            //
            TimeUnit bottomUnit = myBottomUnit;// next.getBottomUnit();
            fireUnitLineStarted(bottomUnit);
            visitTimeUnits(next, bottomUnit);
            fireUnitLineFinished(bottomUnit);
            fireFrameFinished(next);
        }
        fireAfterProcessingTimeFrames();
        myPainter.setGraphics(g);
        // Painter p = new StyledPainterImpl(g, getChartUIConfiguration());
        myChartHeader.getPrimitiveContainer().paint(myPainter, g);
        myBottomUnitLineRenderer.getPrimitiveContainer().paint(myPainter, g);
        Graphics mainArea = g.create(0, getChartUIConfiguration()
                .getHeaderHeight(), (int) getBounds().getWidth(),
                (int) getBounds().getHeight());
        myPainter.setGraphics(mainArea);
        // p = new StyledPainterImpl(mainArea, getChartUIConfiguration());
        myBackgroundRenderer.getPrimitiveContainer().paint(myPainter, g);
        paintMainArea(mainArea, myPainter);
        // Graphics resourcesArea = g.create((int)getBounds().getWidth()-20,
        // getChartUIConfiguration().getHeaderHeight(), 20,
        // (int)getBounds().getHeight());
        // myResourcesRendererImpl.getPrimitiveContainer().paint(p,
        // resourcesArea);
        // myTaskProgressRendererImpl.getPrimitiveContainer().paint(p,
        // mainArea);
    }

    void fireBeforeProcessingTimeFrames() {
        myBackgroundRenderer.beforeProcessingTimeFrames();
        for (int i = 0; i < myTimeUnitVisitors.size(); i++) {
            TimeUnitVisitor nextVisitor = (TimeUnitVisitor) myTimeUnitVisitors
                    .get(i);
            if (!nextVisitor.isEnabled()) {
                continue;
            }
            nextVisitor.beforeProcessingTimeFrames();
        }
    }

    void fireAfterProcessingTimeFrames() {
        for (int i = 0; i < myTimeUnitVisitors.size(); i++) {
            TimeUnitVisitor nextVisitor = (TimeUnitVisitor) myTimeUnitVisitors
                    .get(i);
            if (!nextVisitor.isEnabled()) {
                continue;
            }
            nextVisitor.afterProcessingTimeFrames();
        }
    }

    void fireFrameStarted(TimeFrame timeFrame) {
        for (int i = 0; i < myTimeUnitVisitors.size(); i++) {
            TimeUnitVisitor nextVisitor = (TimeUnitVisitor) myTimeUnitVisitors
                    .get(i);
            if (!nextVisitor.isEnabled()) {
                continue;
            }
            nextVisitor.startTimeFrame(timeFrame);
        }
    }

    void fireFrameFinished(TimeFrame timeFrame) {
        for (int i = 0; i < myTimeUnitVisitors.size(); i++) {
            TimeUnitVisitor nextVisitor = (TimeUnitVisitor) myTimeUnitVisitors
                    .get(i);
            if (!nextVisitor.isEnabled()) {
                continue;
            }
            nextVisitor.endTimeFrame(timeFrame);
        }
    }

    void fireUnitLineStarted(TimeUnit timeUnit) {
        for (int i = 0; i < myTimeUnitVisitors.size(); i++) {
            TimeUnitVisitor nextVisitor = (TimeUnitVisitor) myTimeUnitVisitors
                    .get(i);
            if (!nextVisitor.isEnabled()) {
                continue;
            }
            nextVisitor.startUnitLine(timeUnit);
        }
    }

    void fireUnitLineFinished(TimeUnit timeUnit) {
        for (int i = 0; i < myTimeUnitVisitors.size(); i++) {
            TimeUnitVisitor nextVisitor = (TimeUnitVisitor) myTimeUnitVisitors
                    .get(i);
            if (!nextVisitor.isEnabled()) {
                continue;
            }
            nextVisitor.endUnitLine(timeUnit);
        }
    }

    void visitTimeUnits(TimeFrame timeFrame, TimeUnit timeUnit) {
        for (int j = 0; j < timeFrame.getUnitCount(timeUnit); j++) {
            for (int i = 0; i < myTimeUnitVisitors.size(); i++) {
                TimeUnitVisitor nextVisitor = (TimeUnitVisitor) myTimeUnitVisitors
                        .get(i);
                if (!nextVisitor.isEnabled()) {
                    continue;
                }
                nextVisitor.nextTimeUnit(j);
            }
        }

    }

    public void setBounds(Dimension bounds) {
        myBounds = bounds;
    }

    public void setStartDate(Date startDate) {
        if (!startDate.equals(myStartDate)) {
            myStartDate = startDate;
            myTimeFrames = null;
        }
    }

    public Date getStartDate() {
        return myStartDate;
    }

    public Date getEndDate() {
        TimeFrame[] timeFrames = getTimeFrames(null);
        // for(int i = 0 ; i<timeFrames.length; i++)
        // System.out.println("< "+timeFrames[i].getStartDate() + "" +
        // timeFrames[i].getFinishDate()+" >");

        TimeFrame last = timeFrames[timeFrames.length - 1];
        return last.getFinishDate();
    }

    public void setBottomUnitWidth(int pixelsWidth) {
    	if (myAtomUnitPixels!=pixelsWidth) {
    		myCachingOffsetCalculator.reset();
    	}
        myAtomUnitPixels = pixelsWidth;
    }

    public void setRowHeight(int rowHeight) {
        getChartUIConfiguration().setRowHeight(rowHeight);
    }

    public void setTopTimeUnit(TimeUnit topTimeUnit) {
        setTopUnit(topTimeUnit);
        myTimeFrames = null;
    }

    public void setBottomTimeUnit(TimeUnit bottomTimeUnit) {
        myBottomUnit = bottomTimeUnit;
        myTimeFrames = null;
    }

    protected UIConfiguration getProjectConfig() {
        return myProjectConfig;
    }
    
    protected Dimension getBounds() {
        return myBounds;
    }

    TimeFrame[] getTimeFrames(Date exactDate) {
        if (myTimeFrames == null) {
            myTimeFrames = calculateTimeFrames(exactDate);
        }
        return myTimeFrames;
    }

    protected int getBottomUnitWidth() {
        return myAtomUnitPixels;
    }

    private TimeFrame[] calculateTimeFrames(Date exactDate) {
        ArrayList result = new ArrayList();
        int totalFramesWidth = 0;
        Date currentDate = myStartDate;
        do {

            TimeFrame currentFrame = myTimeUnitStack.createTimeFrame(
                    currentDate, getTopUnit(currentDate), myBottomUnit);
            if (exactDate != null
                    && currentFrame.getStartDate().before(exactDate)) {
                currentFrame.trimLeft(exactDate);
            }
            if (currentFrame.getStartDate().after(currentFrame.getFinishDate())) {
                throw new IllegalStateException("Frame is invalid:\n"+currentFrame+"\n date="+exactDate);
            }
            result.add(currentFrame);
            int frameWidth = myFrameWidthFunction
                    .getTimeFrameWidth(currentFrame);
            totalFramesWidth += frameWidth;
            currentDate = currentFrame.getFinishDate();

        } while (totalFramesWidth <= getBounds().getWidth());
        //
        return (TimeFrame[]) result.toArray(new TimeFrame[0]);
    }

    public GPCalendar.DayType getDayType(TimeFrame timeFrame,
            TimeUnit timeUnit, int unitIndex) {
        Date startDate = timeFrame.getUnitStart(timeUnit, unitIndex);
        Date endDate = timeFrame.getUnitFinish(timeUnit, unitIndex);
        Calendar c = (Calendar) Calendar.getInstance().clone();
        c.setTime(startDate);
        int startDayOfWeek = c.get(Calendar.DAY_OF_WEEK);
        c.setTime(endDate);
        int endDayOfWeek = c.get(Calendar.DAY_OF_WEEK);

        // return startDayOfWeek==Calendar.SATURDAY ||
        // startDayOfWeek==Calendar.SUNDAY;
        // return getTaskManager().getCalendar().getWeekDayType(startDayOfWeek);

        return getTaskManager().getCalendar().getDayTypeDate(startDate);
    }

    public void startDateChanged(ChartViewState.ViewStateEvent e) {
        setStartDate((Date) e.getNewValue());
    }

    public void zoomChanged(ZoomEvent e) {
    }

    /**
     * @author bard
     */
    private interface TimeFrameWidthFunction {
        int getTimeFrameWidth(TimeFrame timeFrame);
    }

    protected TimeUnit getBottomUnit() {
        return myBottomUnit;
    }

    protected TimeUnitStack getTimeUnitStack() {
        return myTimeUnitStack;
    }

    protected DayTypeAlternance[] getDayTypeAlternance(TimeFrame timeFrame,
            TimeUnit timeUnit, int unitIndex) {
        class AlternanceFactory {
            private Calendar c = (Calendar) Calendar.getInstance().clone();

            DayTypeAlternance createAlternance(TimeUnit timeUnit,
                    Date startDate, Date endDate) {
                c.setTime(startDate);
                int startDayOfWeek = c.get(Calendar.DAY_OF_WEEK);
                c.setTime(endDate);
                int endDayOfWeek = c.get(Calendar.DAY_OF_WEEK);
                TaskLength duration = myTaskManager.createLength(timeUnit,
                        startDate, endDate);
                DayType dayType = getTaskManager().getCalendar()
                        .getWeekDayType(startDayOfWeek);
                dayType = getTaskManager().getCalendar().getDayTypeDate(
                        startDate);
                return new DayTypeAlternance(dayType, duration, endDate);
            }
            void createAlternance(TimeUnit timeUnit, TimeFrame timeFrame, List output) {
                DayType startType = null;
                Date startDate = null;
                int unitCount = timeFrame.getUnitCount(timeUnit);
                for (int i=0; i<unitCount; i++) {
                    Date start = timeFrame.getUnitStart(timeUnit, i);
                    c.setTime(start);
                    int startDayOfWeek = c.get(Calendar.DAY_OF_WEEK);
                    DayType dayType = getTaskManager().getCalendar().getWeekDayType(startDayOfWeek);
                    if (startType==null) {
                        startType = dayType;
                        startDate = start;
                    }
                    if (startType!=dayType) {
                        Date end = timeFrame.getUnitFinish(timeUnit,i-1);
                        TaskLength duration = myTaskManager.createLength(timeUnit, startDate, end);
                        output.add(new DayTypeAlternance(startType, duration, end));
                        startType = dayType;
                        startDate = start;
                    }
                }
                Date end = timeFrame.getUnitFinish(timeUnit,unitCount-1);
                TaskLength duration = myTaskManager.createLength(timeUnit, startDate, end);
                output.add(new DayTypeAlternance(startType, duration, end));
                
            }
            
        }
        AlternanceFactory f = new AlternanceFactory();

        DayTypeAlternance[] result;
        Date startDate = timeFrame.getUnitStart(timeUnit, unitIndex);
        Date endDate = timeFrame.getUnitFinish(timeUnit, unitIndex);

        if (timeUnit.equals(myTimeUnitStack.getDefaultTimeUnit())) {
            result = new DayTypeAlternance[] { f.createAlternance(timeUnit,
                    startDate, endDate) };
        } else if (timeUnit.isConstructedFrom(myTimeUnitStack
                .getDefaultTimeUnit())) {
            java.util.List buf = new ArrayList();
            TimeUnit defaultUnit = myTimeUnitStack.getDefaultTimeUnit();
            TimeFrame innerFrame = myTimeUnitStack.createTimeFrame(startDate,
                    timeUnit, defaultUnit);
            // System.err.println("[ChartModelImpl] topUnit="+timeUnit+"
            // bottom="+defaultUnit+"
            // count="+innerFrame.getUnitCount(defaultUnit));
            f.createAlternance(defaultUnit, innerFrame, buf);
            result = (DayTypeAlternance[]) buf
                    .toArray(new DayTypeAlternance[buf.size()]);
        } else {
            throw new RuntimeException("We should not be here");
        }
        // System.err.println("from "+startDate+" to
        // "+endDate+"\n"+java.util.Arrays.asList(result));
        return result;
    }

    Offset[] calculateOffsets(TimeFrame timeFrame, TimeUnit frameBottomUnit, Date bottomUnitStartDate, TimeUnit offsetUnit, int frameBottomUnitWidth) {
        return myCachingOffsetCalculator.calculateOffsets(timeFrame, frameBottomUnit, bottomUnitStartDate, offsetUnit, frameBottomUnitWidth);
    }
    
    //private final OffsetCalculatorImpl myOffsetCalculator;
    protected final ChartUIConfiguration myChartUIConfiguration;

    public ChartUIConfiguration getChartUIConfiguration() {
        return myChartUIConfiguration;
    }

    private class RegularFramesWithFunction implements TimeFrameWidthFunction {
        public int getTimeFrameWidth(TimeFrame timeFrame) {
            return timeFrame.getUnitCount(myBottomUnit) * myAtomUnitPixels;
        }
    }

    private class SkewedFramesWidthFunction implements TimeFrameWidthFunction {
        private float myWidthPerDefaultUnit;

        void initialize() {
            int defaultUnitsPerBottomUnit = myBottomUnit
                    .getAtomCount(myTimeUnitStack.getDefaultTimeUnit());
            myWidthPerDefaultUnit = (float) myAtomUnitPixels
                    / defaultUnitsPerBottomUnit;
        }

        public int getTimeFrameWidth(TimeFrame timeFrame) {
            int defaultUnitsPerTopUnit = timeFrame.getUnitCount(myTimeUnitStack
                    .getDefaultTimeUnit());
            return (int) (defaultUnitsPerTopUnit * myWidthPerDefaultUnit);
        }

    }

    int getBottomUnitWidth(TimeFrame nextFrame) {
        int frameWidth = myFrameWidthFunction.getTimeFrameWidth(nextFrame);
        int bottomUnitsCount = nextFrame
                .getUnitCount(nextFrame.getBottomUnit());
        // System.err.println("ChartModelImpl: getBottomUnitWidth:
        // nextFrame="+nextFrame+" width="+frameWidth+"
        // bottomUnitsCount="+bottomUnitsCount);
        return frameWidth / bottomUnitsCount;
    }

    protected final TaskManager myTaskManager;

    private int myVerticalOffset;

    protected TaskManager getTaskManager() {
        return myTaskManager;
    }

    public ChartHeader getChartHeader() {
        return myChartHeader;
    }

    protected ChartGridImpl getChartGrid() {
        return myChartGrid;
    }

    public float calculateLength(int fromX, int toX, int y) {
        // return toX - fromX;

        int curX = fromX;
        int totalPixels = toX - fromX;
        int holidayPixels = 0;
        while (curX < toX) {
            GraphicPrimitiveContainer.GraphicPrimitive nextPrimitive = getChartGrid()
                    .getPrimitiveContainer().getPrimitive(curX,
                            y - getChartUIConfiguration().getHeaderHeight());
            if (nextPrimitive instanceof GraphicPrimitiveContainer.Rectangle
                    && GPCalendar.DayType.WEEKEND == nextPrimitive
                            .getModelObject()) {
                GraphicPrimitiveContainer.Rectangle nextRect = (Rectangle) nextPrimitive;
                holidayPixels += nextRect.getRightX() - curX;
                if (nextRect.myLeftX < curX) {
                    holidayPixels -= curX - nextRect.myLeftX;
                }
                if (nextRect.myLeftX < fromX) {
                    holidayPixels -= fromX - nextRect.myLeftX;
                }
                if (nextRect.getRightX() > toX) {
                    holidayPixels -= nextRect.getRightX() - toX;
                }
                curX = nextRect.getRightX() + 1;
            } else {
                curX += getBottomUnitWidth();
            }
        }
        float workPixels = (float) totalPixels - (float) holidayPixels;
        return workPixels / (float) getBottomUnitWidth();
    }

    public float calculateLengthNoWeekends(int fromX, int toX) {
        int totalPixels = toX - fromX;
        return totalPixels / (float) getBottomUnitWidth();
    }

    /**
     * @return A length of the visible part of this chart area measured in the
     *         bottom line time units
     */
    public TaskLength getVisibleLength() {
        double pixelsLength = getBounds().getWidth();
        float unitsLength = (float) (pixelsLength / getBottomUnitWidth());
        TaskLength result = getTaskManager().createLength(getBottomUnit(),
                unitsLength);
        return result;
    }

    public void setHeaderHeight(int i) {
        getChartUIConfiguration().setHeaderHeight(i);
    }

    public void setVerticalOffset(int offset) {
        myVerticalOffset = offset;
    }
    
    protected int getVerticalOffset() {
        return myVerticalOffset;
    }
    
    private void setTopUnit(TimeUnit myTopUnit) {
        this.myTopUnit = myTopUnit;
    }

    private TimeUnit getTopUnit() {
        return getTopUnit(myStartDate);
    }

    private TimeUnit getTopUnit(Date startDate) {
        TimeUnit result = myTopUnit;
        if (myTopUnit instanceof TimeUnitFunctionOfDate) {
            if (startDate == null) {
                throw new RuntimeException("No date is set");
            } else {
                result = ((TimeUnitFunctionOfDate) myTopUnit)
                        .createTimeUnit(startDate);
            }
        }
        return result;
    }

    public GPOptionGroup[] getChartOptionGroups() {
        return new GPOptionGroup[] {myChartGrid.getOptions()};
    }

    public void addOptionChangeListener(GPOptionChangeListener listener) {
        myOptionListeners.add(listener);
    }

    protected void fireOptionsChanged() {
        for (int i = 0; i < myOptionListeners.size(); i++) {
            GPOptionChangeListener next = (GPOptionChangeListener) myOptionListeners
                    .get(i);
            next.optionsChanged();
        }
    }

    public ChartModelBase createCopy() {
        return new ChartModelBase(getTaskManager(), getTimeUnitStack(), myProjectConfig);
    }

    public OptionEventDispatcher getOptionEventDispatcher() {
        return myOptionEventDispatcher;
    }
    
    public class OptionEventDispatcher {
        void optionsChanged() {
            fireOptionsChanged();
        }
    }
    

    public static class Offset {
        private TaskLength myOffsetLength;
        private Date myOffsetAnchor;
        private Date myOffsetEnd;
        private int myOffsetPixels;
        private TimeUnit myOffsetUnit;
        Offset(TimeUnit offsetUnit, Date offsetAnchor, Date offsetEnd, int offsetPixels) {
            //myOffsetLength = myTaskManager.createLength(offsetUnit, offsetAnchor, offsetEnd);
            myOffsetAnchor = offsetAnchor;
            myOffsetEnd = offsetEnd;
            myOffsetPixels = offsetPixels;
            myOffsetUnit = offsetUnit;
        }
//        TaskLength getOffsetLength() {
//            return null;
//        }
        Date getOffsetAnchor() {
            return myOffsetAnchor;
        }
        public Date getOffsetEnd() {
            return myOffsetEnd;
        }
        public int getOffsetPixels() {
            return myOffsetPixels;
        }
        TimeUnit getOffsetUnit() {
            return myOffsetUnit;
        }
        
        
    }
}
