/*
 * The MIT License
 *
 * Copyright 2019 Restricted.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

import com.github.lgooddatepicker.components.DatePicker;
import com.github.lgooddatepicker.components.DatePickerSettings;
import com.github.lgooddatepicker.zinternaltools.Pair;
import java.awt.Color;
import java.awt.event.WindowEvent;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Before;

public class TestGithubIssues {
    DatePicker date_picker;

    @Before
    public void setUp()
    {
        date_picker = new DatePicker();
    }

    @Test( expected = Test.None.class /* no exception expected */ )
    public void TestIssue82() throws InterruptedException
    {
        // The exception that might be thrown by the date picker control
        // will be thrown in an AWT-EventQueue thread. To be able to detect
        // these exceptions we register an UncaughtExceptionHandler that
        // writes all occurring exceptions into exInfo.
        // As a result we always have access to the latest thrown exception
        // from any running thread
        final ExceptionInfo exInfo = new ExceptionInfo();
        try {
            RegisterUncaughtExceptionHandlerToAllThreads(new Thread.UncaughtExceptionHandler() {
                @Override
                public void uncaughtException( Thread t, Throwable e ) {
                    exInfo.set(t.getName(), e);
                }
            });
            JFrame testWin = new JFrame();
            testWin.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            testWin.add(date_picker);
            testWin.pack();
            testWin.setVisible(true);
            Thread.sleep(10);
            assertFalse("DatePicker must not have an open popup.", date_picker.isPopupOpen());
            Thread.sleep(10);
            date_picker.openPopup();
            Thread.sleep(10);
            assertTrue("DatePicker must have an open popup.", date_picker.isPopupOpen());
            testWin.dispatchEvent(new WindowEvent(testWin, WindowEvent.WINDOW_CLOSING));
            Thread.sleep(50);
            assertFalse("Exception in antother Thread triggered:\n"
                    +"ThreadName: "+exInfo.getThreadName()+"\n"
                    +"Exception: "+exInfo.getExceptionMessage()
                    +"\nStacktrace:\n"+exInfo.getStackTrace()
                    , exInfo.wasSet());
            } finally {
                RegisterUncaughtExceptionHandlerToAllThreads(null);
        }
    }

    @Test( expected = Test.None.class /* no exception expected */ )
    public void TestIssue76()
    {
        DatePickerSettings dateSettingsPgmDate = new DatePickerSettings();
        dateSettingsPgmDate.setAllowEmptyDates(true);
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy").withLocale(Locale.ENGLISH);
        dateSettingsPgmDate.setFormatForDatesCommonEra(dateFormatter);
        date_picker.setSettings(dateSettingsPgmDate);
        date_picker.getComponentDateTextField().setEditable(false);
        date_picker.getComponentDateTextField().setToolTipText("The earliest date for booking to display.");
        date_picker.getComponentToggleCalendarButton().setToolTipText("The earliest date for booking to display.");
        date_picker.getComponentToggleCalendarButton().setText("+");
        date_picker.getComponentToggleCalendarButton().setFont(new java.awt.Font("Arial", java.awt.Font.PLAIN, 12));
        date_picker.getComponentDateTextField().setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
        date_picker.getComponentDateTextField().setFont(new java.awt.Font("Arial", java.awt.Font.PLAIN, 12));
        date_picker.getComponentDateTextField().setDisabledTextColor(Color.BLACK);
        date_picker.setBounds(115, 50, 160, 20);
        date_picker.setDateToToday();
        date_picker.setText("sun, 11 Aug 2019");
        AssertDateTextValidity(false);
        date_picker.setText("Sun, 11 Aug 2019");
        AssertDateTextValidity(true);
        date_picker.setText("Mon, 11 Aug 2019");
        AssertDateTextValidity(false);
        date_picker.setText("Tue, 30 Apr 2019");
        AssertDateTextValidity(true);
        date_picker.setText("Wed, 31 Apr 2019");
        AssertDateTextValidity(false);
    }

    @Test( expected = Test.None.class /* no exception expected */ )
    public void TestIssue74()
    {
        DateTimeFormatter era_date = DateTimeFormatter.ofPattern("dd/MM/yyyy");
        date_picker.getSettings().setFormatForDatesCommonEra(era_date);
        date_picker.setText("12/31/2019");
        AssertDateTextValidity(false);
        date_picker.setText("31/12/2019");
        AssertDateTextValidity(true);
        date_picker.setText("31/04/2019");
        AssertDateTextValidity(false);
        date_picker.setText("30/04/2019");
        AssertDateTextValidity(true);
    }

    @Test( expected = Test.None.class /* no exception expected */ )
    public void TestIssue60()
    {
        DateTimeFormatter era_date = DateTimeFormatter.ofPattern("ddMMyyyy");
        date_picker.getSettings().setFormatForDatesCommonEra(era_date);
        date_picker.setText("30 04 2019");
        AssertDateTextValidity(false);
        date_picker.setText("30042019");
        AssertDateTextValidity(true);
        date_picker.setText("31042019");
        AssertDateTextValidity(false);
        date_picker.setText(" 30042019 ");
        AssertDateTextValidity(true);
    }

    // helper functions

    private void AssertDateTextValidity(boolean isDateTexValid)
    {
        final Color invalidDate = date_picker.getSettings().getColor(DatePickerSettings.DateArea.DatePickerTextInvalidDate);
        final Color validDate = date_picker.getSettings().getColor(DatePickerSettings.DateArea.DatePickerTextValidDate);
        assertTrue("Foreground colors must be different for this test to work", invalidDate != validDate);
        final Color textfieldcolor = date_picker.getComponentDateTextField().getForeground();
        if (isDateTexValid) {
            assertTrue("False negative! Text should be accpeted: "+date_picker.getText(), textfieldcolor == validDate);
            assertTrue("False negative! Text should be accepted: "+date_picker.getText(), textfieldcolor != invalidDate);
        }
        else {
            assertTrue("False positive! Text should not be accepted: "+date_picker.getText(), textfieldcolor == invalidDate);
            assertTrue("False positive! Text should not be accepted: "+date_picker.getText(), textfieldcolor != validDate);
        }
    }

    static private void RegisterUncaughtExceptionHandlerToAllThreads(Thread.UncaughtExceptionHandler handler)
    {
        Thread.setDefaultUncaughtExceptionHandler(handler);
        //activeCount is only an estimation
        int activeCountOversize = 1;
        Thread[] threads;
        do {
          threads = new Thread[Thread.activeCount() + activeCountOversize];
          Thread.enumerate(threads);
          activeCountOversize++;
        } while (threads[threads.length-1] != null);
        for (Thread thread : threads) {
          if (thread != null) {
              thread.setUncaughtExceptionHandler(handler);
          }
        }
    }

    private class ExceptionInfo
    {
        Pair<String, Throwable> info = new Pair<>("", null);

        synchronized boolean wasSet() {
            return !info.first.isEmpty() || info.second != null;
        }
        synchronized void set(String threadname, Throwable ex) {
            info.first = threadname;
            info.second = ex;
        }
        synchronized String getThreadName() {
            return info.first;
        }
        synchronized String getExceptionMessage() {
            return info.second != null ? info.second.getMessage() : "";
        }
        synchronized String getStackTrace()
        {
            String result = "";
            if (info.second != null) {
                for (StackTraceElement elem : info.second.getStackTrace()) {
                    result += elem.toString()+"\n";
                }
            }
            return result;
        }
    }

}