package com.qmth.boot.tools.excel.parser.impl;

import com.qmth.boot.tools.excel.filter.SheetFilter;
import com.qmth.boot.tools.excel.handler.RowDataHandler;
import com.qmth.boot.tools.excel.model.RowData;
import com.qmth.boot.tools.excel.parser.ExcelParser;
import org.apache.poi.hssf.eventusermodel.*;
import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
import org.apache.poi.hssf.record.*;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class XlsParser implements HSSFListener, ExcelParser {

    private SSTRecord sstRecord;

    private FormatTrackingHSSFListener formatListener;

    private RowDataHandler handler;

    private RowData rowData;

    private SheetFilter sheetFilter;

    private int sheetIndex = -1;

    private boolean sheetAccept = false;

    private List<String> sheetNames = new ArrayList<>();

    private int maxRowIndex = 0;

    public void parse(InputStream inputStream, SheetFilter sheetFilter, RowDataHandler handler) throws Exception {
        this.handler = handler;
        this.sheetFilter = sheetFilter;

        MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this);
        formatListener = new FormatTrackingHSSFListener(listener);

        HSSFEventFactory factory = new HSSFEventFactory();
        HSSFRequest request = new HSSFRequest();
        request.addListenerForAllRecords(formatListener);

        factory.processWorkbookEvents(request, new POIFSFileSystem(inputStream));
    }

    @Override
    public String[] getColumnNames() {
        return handler.getColumnNames();
    }

    @Override
    public void processRecord(Record record) {
        switch (record.getSid()) {
        // the BOFRecord can represent either the beginning of a sheet or the workbook
        case BOFRecord.sid:
            BOFRecord bof = (BOFRecord) record;
            if (bof.getType() == BOFRecord.TYPE_WORKBOOK) {
                //System.out.println("Encountered workbook");
                //assigned to the class level member
            } else if (bof.getType() == BOFRecord.TYPE_WORKSHEET) {
                sheetIndex++;
                sheetAccept = sheetFilter.match(sheetIndex, sheetNames.get(sheetIndex));
                if (sheetAccept) {
                    maxRowIndex = 0;
                    rowData = null;
                }
                //System.out.println("Encountered sheet reference");
            }
            break;
        case BoundSheetRecord.sid:
            BoundSheetRecord bsr = (BoundSheetRecord) record;
            sheetNames.add(bsr.getSheetname());
            break;
        case RowRecord.sid:
            if (sheetAccept) {
                RowRecord rowrec = (RowRecord) record;
                maxRowIndex = Math.max(maxRowIndex, rowrec.getRowNumber());
            }
            break;
        case BlankRecord.sid:
            //BlankRecord brec = (BlankRecord) record;
            //rowData.addValue(brec.getColumn(), null);
            break;
        case NumberRecord.sid:
            if (sheetAccept) {
                NumberRecord numrec = (NumberRecord) record;
                getRowData(numrec.getRow()).addValue(numrec.getColumn(), formatListener.formatNumberDateCell(numrec));
            }
            break;
        // SSTRecords store a array of unique strings used in Excel.
        case SSTRecord.sid:
            sstRecord = (SSTRecord) record;
            break;
        case LabelRecord.sid:
            if (sheetAccept) {
                LabelRecord lrec = (LabelRecord) record;
                getRowData(lrec.getRow()).addValue(lrec.getColumn(), lrec.getValue());
            }
            break;
        case LabelSSTRecord.sid:
            LabelSSTRecord lsrec = (LabelSSTRecord) record;
            if (sheetAccept && sstRecord != null) {
                getRowData(lsrec.getRow())
                        .addValue(lsrec.getColumn(), sstRecord.getString(lsrec.getSSTIndex()).toString());
            }
            break;
        }
        // Handle missing column
        //if (record instanceof MissingCellDummyRecord) {
        //			MissingCellDummyRecord mc = (MissingCellDummyRecord)record;
        // thisStr = "";
        //}

        // Handle end of row
        if (record instanceof LastCellOfRowDummyRecord) {
            //End the row
            if (sheetAccept && rowData != null) {
                handler.handle(rowData);
                rowData = null;
            }
        }
    }

    private RowData getRowData(int row) {
        if (rowData == null) {
            rowData = new RowData(row);
        }
        return rowData;
    }
}
