package hu.astrid.debgen.labelprint.printer;

import hu.astrid.debgen.labelprint.domain.Label;
import hu.astrid.debgen.labelprint.exception.LabelPrintException;
import org.easymock.EasyMock;
import static org.easymock.EasyMock.*;
import org.easymock.IArgumentMatcher;
import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import org.junit.Test;

import javax.print.*;
import javax.print.attribute.PrintRequestAttributeSet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;

/**
 * Astrid Research
 * Author: inagy
 * Created: Jan 11, 2010 4:05:11 PM
 */
public class ZebraLabelPrinterTest {

    private PrintService createPrintServiceMock(String printData) {
        PrintService printService = createMock(PrintService.class);
        expect(printService.getName()).andReturn(printData);
        replay(printService);
        return printService;
    }

    @Test
    public void testSearchZebraPrinter() throws LabelPrintException {

        PrintService printService1 = createPrintServiceMock("print123");
        PrintService printService2 = createPrintServiceMock("printZEBRA123");

        TestableZebraLabelPrinter.PrintServicesFactory factoryMock = createMock(TestableZebraLabelPrinter.PrintServicesFactory.class);
        expect(factoryMock.lookupPrintServices(null, null)).andReturn(new PrintService[]{printService1, printService2});
        replay(factoryMock);

        TestableZebraLabelPrinter printer = new TestableZebraLabelPrinter();
        printer.setPrintServicesFactory(factoryMock);

        PrintService service = printer.searchZebraPrinter();
        assertSame(printService2, service);
    }

    @Test
    public void testSearchZebraPrinter_not_found() throws LabelPrintException {

        PrintService printService1 = createPrintServiceMock("print123");
        PrintService printService2 = createPrintServiceMock("print456");

        TestableZebraLabelPrinter.PrintServicesFactory factoryMock = createMock(TestableZebraLabelPrinter.PrintServicesFactory.class);
        expect(factoryMock.lookupPrintServices(null, null)).andReturn(new PrintService[]{printService1, printService2});
        replay(factoryMock);

        TestableZebraLabelPrinter printer = new TestableZebraLabelPrinter();
        printer.setPrintServicesFactory(factoryMock);

        PrintService service = printer.searchZebraPrinter();
        assertNull(service);
    }

    @Test
    public void testSearchZebraPrinter_lowercase() throws LabelPrintException {

        PrintService printService1 = createPrintServiceMock("45zebRa6");
        PrintService printService2 = createPrintServiceMock("print123");

        TestableZebraLabelPrinter.PrintServicesFactory factoryMock = createMock(TestableZebraLabelPrinter.PrintServicesFactory.class);
        expect(factoryMock.lookupPrintServices(null, null)).andReturn(new PrintService[]{printService1, printService2});
        replay(factoryMock);

        TestableZebraLabelPrinter printer = new TestableZebraLabelPrinter();
        printer.setPrintServicesFactory(factoryMock);

        PrintService service = printer.searchZebraPrinter();
        assertSame(printService1, service);
    }

    private DocPrintJob createJobMock(String msg) throws PrintException {
        DocPrintJob jobMock = createMock(DocPrintJob.class);
        jobMock.print(eqDoc(msg.getBytes()), (PrintRequestAttributeSet) isNull());
        expectLastCall();
        replay(jobMock);
        return jobMock;
    }

    private Label createLabelMock(String str) throws LabelPrintException {
        Label labelMock = createMock(Label.class);
        expect(labelMock.processTemplate()).andReturn(str);
        replay(labelMock);
        return labelMock;
    }


//    TODO: Javítani kell a tesztet.
//    @Test
//    public void testPrintLabel() throws Exception {
//        TestableZebraLabelPrinter printer = new TestableZebraLabelPrinter();
//
//        DocPrintJob jobMock = createJobMock("template");
//
//        PrintService serviceMock = createMock(PrintService.class);
//        expect(serviceMock.createPrintJob()).andReturn(jobMock);
//        replay(serviceMock);
//
//        printer.setPrintService(serviceMock);
//
//        Label labelMock = createLabelMock("template");
//        printer.printLabel(Arrays.asList(labelMock));
//
//        verify(jobMock);
//    }

    @Test
    public void testPrintLabel_empty() throws Exception {
        TestableZebraLabelPrinter printer = new TestableZebraLabelPrinter();

        DocPrintJob jobMock = createMock(DocPrintJob.class);
        replay(jobMock);

        PrintService serviceMock = createMock(PrintService.class);
        expect(serviceMock.createPrintJob()).andReturn(jobMock);
        replay(serviceMock);

        printer.setPrintService(serviceMock);

        printer.printLabel(new ArrayList<Label>());

        verify(jobMock); // 0 call to job.print() is expected

    }


//    TODO: javítani a tesztet
//    @Test
//    public void testPrintLabel_more() throws Exception {
//        TestableZebraLabelPrinter printer = new TestableZebraLabelPrinter();
//
//        DocPrintJob jobMock1 = createJobMock("template");
//        DocPrintJob jobMock2 = createJobMock("testprint");
//
//        PrintService serviceMock = createMock(PrintService.class);
//        expect(serviceMock.createPrintJob()).andReturn(jobMock1);
//        expect(serviceMock.createPrintJob()).andReturn(jobMock2);
//        replay(serviceMock);
//
//        printer.setPrintService(serviceMock);
//
//        Label labelMock = createLabelMock("template");
//        Label labelMock2 = createLabelMock("testprint");
//
//        printer.printLabel(Arrays.asList(labelMock, labelMock2));
//
//        verify(jobMock1);
//        verify(jobMock2);
//
//    }

    /**
     * This method can be used in an EasyMock.expect call, to use a DocMatcher, to check if the
     * argument is a SimpleDoc with the proper printData value.
     *
     * @param printData - expected data to compare with the actual call
     * @return
     */
    public static Doc eqDoc(byte[] printData) {
        EasyMock.reportMatcher(new DocMatcher(printData));
        return null;
    }

    static class DocMatcher implements IArgumentMatcher {

        private byte[] printData;

        DocMatcher(byte[] printData) {
            this.printData = printData;
        }

        @Override
        public boolean matches(Object o) {
            if (!(o instanceof SimpleDoc)) {
                return false;
            }
            SimpleDoc other = (SimpleDoc) o;
            Object otherPrintData;
            try {
                otherPrintData = other.getPrintData();
            } catch (IOException e) {
                return false;
            }
            if (!(otherPrintData instanceof byte[])) {
                return false;
            }
            return Arrays.equals((byte[]) otherPrintData, printData);
        }

        @Override
        public void appendTo(StringBuffer buffer) {
            buffer.append("eqDoc(\"");
            buffer.append(new String(printData)).append("\" - ").append(Arrays.toString(printData));
            buffer.append(")");
        }

    }
}
