/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2008 jOpenDocument, by ILM Informatique. All rights reserved.
 * 
 * The contents of this file are subject to the terms of the GNU
 * General Public License Version 3 only ("GPL").  
 * You may not use this file except in compliance with the License. 
 * You can obtain a copy of the License at http://www.gnu.org/licenses/gpl-3.0.html
 * See the License for the specific language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each file.
 * 
 */

package org.jopendocument.dom.spreadsheet;

import org.jopendocument.dom.NS;
import org.jopendocument.dom.ODPackage;
import org.jopendocument.dom.OOUtils;
import org.jopendocument.dom.OOXML;

import java.awt.Point;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Calendar;

import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;

import junit.framework.TestCase;

import org.xml.sax.SAXException;

public class SheetTest extends TestCase {

    static final DefaultTableModel tm;
    static {
        tm = new DefaultTableModel();
        tm.addColumn("col1");
        tm.addColumn("col2");
        tm.addColumn("col3");

        tm.addRow(new Object[] { "un1", new Float(-5.32), new Integer(123) });
    }

    private Sheet sheet;
    private Sheet realSheet;

    protected void setUp() throws Exception {
        this.sheet = SpreadSheet.createEmpty(tm, NS.getOOo()).getSheet(0);
        this.realSheet = SpreadSheet.create(new ODPackage(this.getClass().getResourceAsStream("test.ods"))).getSheet(0);
    }

    protected void tearDown() throws Exception {
        this.sheet = null;
        this.realSheet = null;
    }

    /*
     * Test method for 'org.jopendocument.dom.spreadsheet.Sheet.setValueAt(Object, int, int)'
     */
    public void testSetValueAt() {
        assertEquals("col1", this.sheet.getValueAt(0, 0));
        this.sheet.setValueAt("test", 0, 0);
        assertEquals("test", this.sheet.getValueAt(0, 0));

        final Calendar now = Calendar.getInstance();
        this.sheet.setValueAt(now.getTime(), 0, 0);
        final Object valueAt = this.sheet.getValueAt(0, 0);
        // OpenDocument can encode time up to the ms
        assertEquals(now.getTime(), valueAt);
    }

    /*
     * Test method for 'org.jopendocument.dom.spreadsheet.Sheet.getValueAt(int, int)'
     */
    public void testGetValueAtIntInt() {
        assertEquals(new Float(123), this.sheet.getValueAt(2, 1));
    }

    /*
     * Test method for 'org.jopendocument.dom.spreadsheet.Sheet.getValueAt(String)'
     */
    public void testGetValueAtString() {
        // oo ne connait les int seulement les float
        assertEquals(new Float(123), this.sheet.getValueAt("C2"));
        assertFalse(new Integer(123).equals(this.sheet.getValueAt("C2")));

        final Calendar c = Calendar.getInstance();
        c.clear();
        c.set(2008, 11, 25);
        assertEquals(c.getTime(), this.realSheet.getValueAt("F3"));
    }

    /*
     * Test method for 'org.jopendocument.dom.spreadsheet.Sheet.getRowCount()'
     */
    public void testGetRowCount() {
        // col labels + 1 row
        assertEquals(2, this.sheet.getRowCount());
    }

    /*
     * Test method for 'org.jopendocument.dom.spreadsheet.Sheet.getColumnCount()'
     */
    public void testGetColumnCount() {
        assertEquals(3, this.sheet.getColumnCount());
    }

    /*
     * Test method for 'org.jopendocument.dom.spreadsheet.Sheet.ensureColumnCount(int)'
     */
    public void testEnsureColumnCount() {
        final int before = this.sheet.getColumnCount();
        final int newSize = before - 2;
        this.sheet.ensureColumnCount(newSize);
        assertEquals(before, this.sheet.getColumnCount());
    }

    public void testSetColumnCount() throws Exception {
        // test in-memory
        final int newSize = this.sheet.getColumnCount() + 5;
        try {
            this.sheet.setValueAt("test", newSize - 1, 0);
            fail("should have thrown IndexOutOfBoundsException");
        } catch (IndexOutOfBoundsException exn) {
            // normal
        }
        this.sheet.setColumnCount(newSize);
        this.sheet.setValueAt("test", newSize - 1, 0);

        // test with real file
        final int origCount = this.realSheet.getColumnCount();
        try {
            this.realSheet.setValueAt("over", origCount + 10 - 1, 1);
            fail("should throw exn since we try to write past the limit");
        } catch (RuntimeException e) {
            // ok
        }
        this.realSheet.setColumnCount(origCount + 10);
        this.realSheet.setValueAt("over", origCount + 10 - 1, 1);
        final ByteArrayOutputStream out = new ByteArrayOutputStream(1024 * 15);
        this.realSheet.getSpreadSheet().getPackage().save(out);
        final SpreadSheet reRead = SpreadSheet.create(new ODPackage(new ByteArrayInputStream(out.toByteArray())));
        assertEquals("over", reRead.getSheet(0).getValueAt(origCount + 10 - 1, 1));

        assertNull(this.isValidXML(this.realSheet.getSpreadSheet()));
    }

    public void testSetRowCount() throws IOException {
        this.realSheet.setRowCount(3);
        final SpreadSheet reParsed = new SpreadSheet(this.realSheet.getODDocument().getContent(), null);
        assertEquals(3, reParsed.getSheet(0).getRowCount());
    }

    /*
     * Test method for 'org.jopendocument.dom.spreadsheet.Sheet.getTableModel(int, int)'
     */
    public void testGetTableModel() {
        TableModel t = this.sheet.getTableModel(1, 0);
        assertEquals(tm.getColumnCount() - 1, t.getColumnCount());
        // +1: col labels
        assertEquals(tm.getRowCount() + 1, t.getRowCount());
        assertEquals(new Float(-5.32), t.getValueAt(1, 0));
    }

    /*
     * Test method for 'org.jopendocument.dom.spreadsheet.Sheet.getMutableTableModel(int, int)'
     */
    public void testGetMutableTableModel() {
        this.sheet.getMutableTableModel(1, 0).setValueAt("test", 0, 1);
        assertEquals("test", this.sheet.getCellAt("C1").getValue());
    }

    /*
     * Test method for 'org.jopendocument.dom.spreadsheet.Sheet.merge(TableModel, int, int)'
     */
    public void testMerge() {

    }

    public void testIsCellRef() {
        assertTrue(Sheet.isCellRef("A23"));
        assertFalse(Sheet.isCellRef("A23A"));
        assertFalse(Sheet.isCellRef("test"));
        assertFalse(Sheet.isCellRef("23"));
    }

    public void testResolve() {
        assertEquals(Sheet.resolve("AA34"), new Point(26, 33));
    }

    public void testToInt() {
        assertEquals(0, Sheet.toInt("A"));
        assertEquals(26, Sheet.toInt("AA"));
        assertEquals(77, Sheet.toInt("BZ"));
    }

    public void testDuplicateRows() throws Exception {
        this.realSheet.duplicateRows(8, 3, 2);
        assertEquals(this.realSheet.getValueAt("B11"), this.realSheet.getValueAt("B14"));
        assertEquals("Comptabilité", this.realSheet.getValueAt("H9"));
        // two copies
        assertEquals(this.realSheet.getValueAt("H9"), this.realSheet.getValueAt("H12"));
        assertEquals(this.realSheet.getValueAt("H9"), this.realSheet.getValueAt("H15"));
    }

    public void testXY() throws Exception {
        final int y = 35;
        {
            final MutableCell c = this.realSheet.getCellAt(2, y);
            assertEquals("LECTURE SEULE", c.getValue());
            assertEquals(2, c.getX());
            assertEquals(y, c.getY());
        }
        // offset by 6
        this.realSheet.duplicateRows(8, 3, 2);

        {
            final MutableCell c = this.realSheet.getCellAt(2, y + 6);
            assertEquals("LECTURE SEULE", c.getValue());
            assertEquals(2, c.getX());
            assertEquals(y + 6, c.getY());
        }
    }

    public void testStyle() throws Exception {
        // even though the style-name is null
        assertNull(this.realSheet.getImmutableCellAt(2, 10).getStyleAttr());
        final String style = this.realSheet.getStyleNameAt(2, 10);
        assertNotNull(style);
        // the correct style can still be found
        assertEquals("#00ffff", this.realSheet.getCellAt(2, 10).getStyle().getBackgroundColor());
        // also from MutableCell
        assertEquals(style, this.realSheet.getCellAt(2, 10).getStyleName());

        // column styles
        assertEquals(54.38f, this.realSheet.getColumn(1).getWidth());
        final SpreadSheet sxc = SpreadSheet.create(new ODPackage(this.getClass().getResourceAsStream("test.sxc")));
        assertEquals(33.85f, sxc.getSheet(0).getColumn(1).getWidth());
    }

    private String isValidXML(SpreadSheet ssheet) throws UnsupportedEncodingException, SAXException {
        return OOXML.get(OOUtils.OD).isValid(ssheet.getContent());
    }
}
