package de.duehl.swing.ui.dragndrop.filler;

/*
 * Copyright 2020 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import de.duehl.swing.ui.dragndrop.DragNDropManagerHelper;

/**
 * Diese Klasse analysiert einen Text darauf, ob er eine Adresse darstellt.
 *
 * Siehe Impressumprojekt
 *     de.heinsundpartner.impressum.editor.ui.elements.manager.filler.AddressTextFiller
 * und die Insolvenztools, die auf
 *     de.heinsundpartner.hr.insolvenzen.ui.InsoGuiBase
 * basieren.
 *
 * @version 1.01     2020-01-27
 * @author Christian Dühl
 */

public class AddressFiller {

    /** Der auf eine Adresse aufzuteilende Text. */
    private final String text;

    /** Gibt an, ob nur Inland-Adressen ohne Land analysiert werden sollen. */
    private boolean onlyInland;

    /** Gibt an, ob eine Adresse gefunden wurde. */
    private boolean foundAddress;

    /** Die Straße, falls eine Adresse gefunden wurde4. */
    private String street;

    /** Die Postleitzahl, falls eine Adresse gefunden wurde4. */
    private String postCode;

    /** Der Ort, falls eine Adresse gefunden wurde4. */
    private String town;

    /** Das Land, falls eine Adresse gefunden wurde4. */
    private String country;

    /** Gibt an, ob analyse() bereits aufgerufen wurde. */
    private boolean called;

    /**
     * Konstruktor.
     *
     * @param text
     *            Der auf eine Adresse aufzuteilende Text.
     */
    public AddressFiller(String text) {
        this.text = DragNDropManagerHelper.prepareHtmlDraggedString(text);

        street = "";
        postCode = "";
        town = "";
        country = "";

        foundAddress = false;
        onlyInland = false;

        /*
         * Wir können nicht einfach selbst
         *     analyze();
         * aufrufen, da sonst das setzen von onlyInland anschließend ohne Wirkung wäre.
         */

        called = false;
    }

    /** Legt fest, dass nur Inland-Adressen ohne Land analysiert werden sollen. */
    public void onlyInland() {
        onlyInland = true;
    }

    /** Führt die Analyse und ggf. das Auffüllen der Felder durch. */
    public void analyze() {
        checkIfCalledTwice();

        if (!foundAddress && !onlyInland) { fillWithLetterAndFullCountryWithKomma(); }
        if (!foundAddress && !onlyInland) { fillWithLetterAndFullCountry();          }
        if (!foundAddress && !onlyInland) { fillWithFullCountryWithKomma();          }
        if (!foundAddress && !onlyInland) { fillWithFullCountry();                   }
        if (!foundAddress               ) { fillWithoutCountry();                    }
        if (!foundAddress               ) { fillWithoutCountryKommaNoSpace();        }
        if (!foundAddress && !onlyInland) { fillWithLetterCountry();                 }
        if (!foundAddress && !onlyInland) { fillWithLetterCountryKommaNoSpace();     }
        /*
         * Wenn es ganz viele werden über Liste von Klassen...
         */
    }

    private void checkIfCalledTwice() {
        if (called) {
            throw new RuntimeException("In der Klasse " + getClass().getName()
                    + " darf analyze() nur einmal aufgerufen werden!");
        }
        else {
            called = true;
        }
    }

    /**
     * Ein Pattern zum Erkennen von Adressen bei Drag'n'Drop. Es ist sehr rudimentär, wenn so etwas
     * wie " 12345 " auftaucht, wird die Zahl als Postleitzahl genommen, alles davor als Straße und
     * alles danach als Ort interpretiert.
     */
    private static final Pattern ADDRESS_PATTERN = Pattern.compile(""
            + "(.*?),?"             // Straße und Hausnummer
            + "\\s+"                // Platz zwischen Postleitzahl und Ort
            + "(\\d{5})"            // Postleitzahl
            + "\\s+"                // Platz zwischen Postleitzahl und Ort
            + "(.*)",               // Ort
            Pattern.DOTALL);

    private void fillWithoutCountry() {
        fillIfMatchesStreet1Plz2Ort3(ADDRESS_PATTERN);
    }

    /**
     * Ein Pattern zum Erkennen von Adressen bei Drag'n'Drop. Es ist sehr rudimentär, wenn so etwas
     * wie " 12345 " auftaucht, wird die Zahl als Postleitzahl genommen, alles davor als Straße und
     * alles danach als Ort interpretiert.
     *
     * Beispiel: Felix_001 #4 "Altenbraker Straße 6,12053 Berlin"
     */
    private static final Pattern ADDRESS_PATTERN_COMMA_NO_SPACE = Pattern.compile(""
            + "(.*?)"               // Straße und Hausnummer
            + ","                   // Komma zwischen Postleitzahl und Ort
            + "(\\d{5})"            // Postleitzahl
            + "\\s+"                // Platz zwischen Postleitzahl und Ort
            + "(.*)",               // Ort
            Pattern.DOTALL);

   private void fillWithoutCountryKommaNoSpace() {
       fillIfMatchesStreet1Plz2Ort3(ADDRESS_PATTERN_COMMA_NO_SPACE);
   }

    /**
     * Ein Pattern zum Erkennen von Adressen bei Drag'n'Drop. Es ist sehr rudimentär, wenn so etwas
     * wie " 12345 " auftaucht, wird die Zahl als Postleitzahl genommen, alles davor als Straße und
     * alles danach als Ort interpretiert.
     */
    private static final Pattern ADDRESS_PATTERN_WITH_D = Pattern.compile(""
            + "(.*?),?"               // Straße und Hausnummer
            + "\\s+"                  // Platz zwischen Straße und Postleitzahl
            + "([A-Z])-(\\d{5})"      // Land und Postleitzahl
            + "\\s+"                  // Platz zwischen Postleitzahl und Ort
            + "(.*)",                 // Ort
            Pattern.DOTALL);

    private void fillWithLetterCountry() {
        fillIfMatchesStreet1Country2Plz3Ort4(ADDRESS_PATTERN_WITH_D);
    }

    /**
     * Ein Pattern zum Erkennen von Adressen bei Drag'n'Drop. Es ist sehr rudimentär, wenn so etwas
     * wie " 12345 " auftaucht, wird die Zahl als Postleitzahl genommen, alles davor als Straße und
     * alles danach als Ort interpretiert.
     */
    private static final Pattern ADDRESS_PATTERN_WITH_LETTER_COUNTRX_KOMMA_NO_SPACE = Pattern.compile(""
            + "(.*?)"                 // Straße und Hausnummer
            + ","                     // Komma zwischen Straße und Postleitzahl
            + "([A-Z])-(\\d{5})"      // Land und Postleitzahl
            + "\\s+"                  // Platz zwischen Postleitzahl und Ort
            + "(.*)",                 // Ort
            Pattern.DOTALL);

    private void fillWithLetterCountryKommaNoSpace() {
        fillIfMatchesStreet1Country2Plz3Ort4(ADDRESS_PATTERN_WITH_LETTER_COUNTRX_KOMMA_NO_SPACE);
    }

    /**
      *  Rhenus-Platz 1
      *  59439 Holzwickede
      *  Germany
      *
      *Beispiel Johannes-001 2
      */
    private static final Pattern ADDRESS_PATTERN_FULL_COUNTRY = Pattern.compile(""
            + "(.*?),?"               // Straße und Hausnummer
            + "\\s+"                  // Platz zwischen Straße und Postleitzahl
            + "(\\d{5})"              // Postleitzahl
            + "\\s+"                  // Platz zwischen Postleitzahl und Ort
            + "(.*),?"                // Ort
            + "\\s+"                  // Platz zwischen Ort und Land
            + "(\\S*)",               // Land
            Pattern.DOTALL);

    private void fillWithFullCountry() {
        fillIfMatchesStreet1Plz2Ort3Country4(ADDRESS_PATTERN_FULL_COUNTRY);
    }

    private static final Pattern ADDRESS_PATTERN_FULL_COUNTRY_WITH_KOMMA = Pattern.compile(""
            + "(.*?),"                // Straße und Hausnummer
            + "\\s*"                  // Platz zwischen Straße und Postleitzahl
            + "(\\d{5})"              // Postleitzahl
            + "\\s+"                  // Platz zwischen Postleitzahl und Ort
            + "(.*),"                 // Ort
            + "\\s*"                  // Platz zwischen Ort und Land
            + "(\\S*)",               // Land
            Pattern.DOTALL);

    private void fillWithFullCountryWithKomma() {
        fillIfMatchesStreet1Plz2Ort3Country4(ADDRESS_PATTERN_FULL_COUNTRY_WITH_KOMMA);
    }

    private static final Pattern ADDRESS_PATTERN_LETTER_AND_FULL_COUNTRY = Pattern.compile(""
            + "(.*?),?"               // Straße und Hausnummer
            + "\\s+"                  // Platz zwischen Straße und Postleitzahl
            + "([A-Z])-(\\d{5})"              // Postleitzahl
            + "\\s+"                  // Platz zwischen Postleitzahl und Ort
            + "(.*),?"                // Ort
            + "\\s+"                  // Platz zwischen Ort und Land
            + "(\\S*)",               // Land
            Pattern.DOTALL);

    private void fillWithLetterAndFullCountry() {
        fillIfMatchesStreet1Country2Plz3Ort4Country5(ADDRESS_PATTERN_LETTER_AND_FULL_COUNTRY);
    }

    private static final Pattern ADDRESS_PATTERN_LETTER_AND_FULL_COUNTRY_WITH_KOMMA = Pattern.compile(""
            + "(.*?),"                // Straße und Hausnummer
            + "\\s*"                  // Platz zwischen Straße und Postleitzahl
            + "([A-Z])-(\\d{5})"              // Postleitzahl
            + "\\s+"                  // Platz zwischen Postleitzahl und Ort
            + "(.*),"                 // Ort
            + "\\s*"                  // Platz zwischen Ort und Land
            + "(\\S*)",               // Land
            Pattern.DOTALL);

    private void fillWithLetterAndFullCountryWithKomma() {
        fillIfMatchesStreet1Country2Plz3Ort4Country5(ADDRESS_PATTERN_LETTER_AND_FULL_COUNTRY_WITH_KOMMA);
    }

    private void fillIfMatchesStreet1Plz2Ort3(Pattern pattern) {
        Matcher matcher = pattern.matcher(text);

        if (matcher.matches()) {
            street = matcher.group(1);
            postCode = matcher.group(2);
            town = matcher.group(3);

            foundAddress = true;
        }
    }

    private void fillIfMatchesStreet1Country2Plz3Ort4(Pattern pattern) {
        Matcher matcher = pattern.matcher(text);

        if (matcher.matches()) {
            street = matcher.group(1).trim();
            country = matcher.group(2).trim();
            postCode = matcher.group(3).trim();
            town = matcher.group(4).trim();

            foundAddress = true;
        }
    }

    private void fillIfMatchesStreet1Plz2Ort3Country4(Pattern pattern) {
        Matcher matcher = pattern.matcher(text);

        if (matcher.matches()) {
            street = matcher.group(1).trim();
            postCode = matcher.group(2).trim();
            town = matcher.group(3).trim();
            country = matcher.group(4).trim();

            foundAddress = true;
        }
    }

    private void fillIfMatchesStreet1Country2Plz3Ort4Country5(Pattern pattern) {
        Matcher matcher = pattern.matcher(text);

        if (matcher.matches()) {
            street = matcher.group(1).trim();
            String letterCountry = matcher.group(2).trim();
            postCode = matcher.group(3).trim();
            town = matcher.group(4).trim();
            String fullCountry = matcher.group(5).trim();
            country = letterCountry + " " + fullCountry;

            foundAddress = true;
        }
    }

    /** Gibt an, ob eine Adresse gefunden wurde. */
    public boolean foundAddress() {
        return foundAddress;
    }

    /** Getter für die Straße, falls eine Adresse gefunden wurde4. */
    public String getStreet() {
        return street;
    }

    /** Getter für die Postleitzahl, falls eine Adresse gefunden wurde4. */
    public String getPostCode() {
        return postCode;
    }

    /** Getter für den Ort, falls eine Adresse gefunden wurde4. */
    public String getTown() {
        return town;
    }

    /** Getter für das Land, falls eine Adresse gefunden wurde4. */
    public String getCountry() {
        return country;
    }

}
