# -*- coding: utf-8 -*-
"""
/***************************************************************************
 jpdata
                                 A QGIS plugin
 Download and load various data of Japan
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2024-04-28
        git sha              : $Format:%H$
        copyright            : (C) 2024-2025 by Yoshihiko Baba
        email                : babayoshihiko@mac.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""
from qgis.PyQt.QtCore import (
    QSettings,
    QTranslator,
    QCoreApplication,
    Qt,
    QUrl,
)
from qgis.PyQt.QtGui import QIcon, QDesktopServices
from qgis.PyQt.QtWidgets import QAction

# Initialize Qt resources from file resources.py
from .resources import *

# Import the code for the DockWidget
from .jpdata_dockwidget import jpdataDockWidget
import os.path

# User defined
import os, tempfile, posixpath
from qgis.PyQt.QtWidgets import (
    QAction,
    QFileDialog,
    QListWidgetItem,
    QLineEdit,
    QAbstractItemView,
)
from qgis.core import (
    QgsProject,
    QgsSettings,
    QgsVectorLayer,
    QgsRasterLayer,
    QgsPointXY,
    QgsCoordinateReferenceSystem,
    QgsCoordinateTransform,
)
from qgis.utils import iface

from . import jpDataUtils
from . import jpDataDownloader
from . import jpDataMuni
from . import jpDataCensus
from . import jpDataLNI
from . import jpDataMesh
from . import jpDataAddr


class jpdata:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface

        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)

        # initialize locale
        locale = QSettings().value("locale/userLocale")[0:2]
        locale_path = posixpath.join(
            self.plugin_dir, "i18n", "jpdata_{}.qm".format(locale)
        )

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr("&jpdata")
        # TODO: We are going to let the user set this up in a future iteration
        # self.toolbar = self.iface.addToolBar("jpdata")
        # self.toolbar.setObjectName("jpdata")

        # print "** INITIALIZING jpdata"

        self.pluginIsActive = False
        self.dockwidget = None

        # User defined
        self._folderPath = QSettings().value("jpdata/FolderPath", "~")
        self._proxyServer = QSettings().value("jpdata/ProxyServer", "http://")
        # self._LandNumInfo = jpDataUtils.getMapsFromCsv()
        self._LandNumInfo2 = jpDataUtils.getMapsFromCsv2()
        self._GSI = jpDataUtils.getTilesFromCsv()
        self._dl_status = ""
        self._dl_url_zip = []
        self._dl_iter = 0
        self._verbose = False
        # Create an action that triggers the folder chooser
        self.action = QAction("Choose Folder", self.iface.mainWindow())
        self.action.triggered.connect(self.chooseFolder)

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate("jpdata", message)

    def add_action(
        self,
        icon_path,
        text,
        callback,
        enabled_flag=True,
        add_to_menu=True,
        parent=None,
    ):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if add_to_menu:
            self.iface.addPluginToMenu(self.menu, action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        self.add_action(
            posixpath.join(self.plugin_dir, "icon.png"),
            text=self.tr("jpdata"),
            callback=self.run,
            parent=self.iface.mainWindow(),
        )

    # --------------------------------------------------------------------------

    def onClosePlugin(self):
        """Cleanup necessary items here when plugin dockwidget is closed"""

        # print "** CLOSING jpdata"

        # disconnects
        self.dockwidget.closingPlugin.disconnect(self.onClosePlugin)

        # remove this statement if dockwidget is to remain
        # for reuse if plugin is reopened
        # Commented next statement since it causes QGIS crashe
        # when closing the docked window:
        # self.dockwidget = None

        self.pluginIsActive = False

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""

        # print "** UNLOAD jpdata"

        for action in self.actions:
            self.iface.removePluginMenu(self.tr("&jpdata"), action)
            # self.iface.removeToolBarIcon(action)
        # remove the toolbar
        # del self.toolbar

    # --------------------------------------------------------------------------

    def run(self):
        """Run method that loads and starts the plugin"""

        if not self.pluginIsActive:
            self.pluginIsActive = True

            # print "** STARTING jpdata"

            # dockwidget may not exist if:
            #    first run of plugin
            #    removed on close (see self.onClosePlugin method)
            if self.dockwidget == None:
                # Create the dockwidget (after translation) and keep reference
                self.dockwidget = jpdataDockWidget()

            # connect to provide cleanup on closing of dockwidget
            self.dockwidget.closingPlugin.connect(self.onClosePlugin)

            # User defined
            self._downloader = jpDataDownloader.DownloadThread()
            self._downloader.setProxyServer(self._proxyServer)
            self._downloader.progress.connect(self.dockwidget.progressBar.setValue)
            self._downloader.finished.connect(self.download_finished)
            self.name_map_prev = ""  # Tab 1 name_map

            self.dockwidget.myPushButton2.setText(self.tr("Choose Folder"))
            self.dockwidget.myPushButton2.setToolTip(self.tr("Choose Folder"))
            self.dockwidget.myPushButton2.clicked.connect(self.chooseFolder)

            self.dockwidget.myLabelStatus.setText("")

            # Set Tab 1
            self.dockwidget.myTabWidget.setTabText(0, self.tr("LandNumInfo"))
            self.dockwidget.myPushButton11.setText(self.tr("Download"))
            self.dockwidget.myPushButton11.setToolTip(
                self.tr("Download Land Numerical Information data")
            )
            self.dockwidget.myPushButton11.clicked.connect(self.tab1DownloadAll)
            self.dockwidget.myPushButton14.setText(self.tr("Add to Map"))
            self.dockwidget.myPushButton14.setToolTip(
                self.tr("Add Shapefile as a Layer to Map on QGIS")
            )
            self.dockwidget.myPushButton14.clicked.connect(self.tab1AddMap)
            self.dockwidget.myPushButton15.setText(self.tr("Web"))
            self.dockwidget.myPushButton15.setToolTip(
                self.tr("Open the webpage with the standard browser")
            )
            self.dockwidget.myPushButton15.clicked.connect(self.tab1Web)
            for thisLandNum in self._LandNumInfo2.values():
                item = QListWidgetItem(thisLandNum["name_j"])
                if thisLandNum["availability"] != "yes":
                    item.setFlags(item.flags() & ~Qt.ItemIsSelectable)
                    if thisLandNum["availability"] == "heading":
                        current_text_color = item.foreground().color()
                        current_bg_color = item.background().color()
                        if current_text_color == current_bg_color:
                            if (
                                current_bg_color == Qt.black
                                or current_bg_color == Qt.darkGray
                            ):
                                current_bg_color = Qt.white
                            else:
                                current_bg_color = Qt.darkGray
                        item.setForeground(current_bg_color)
                        item.setBackground(current_text_color)
                    else:
                        item.setForeground(Qt.gray)
                self.dockwidget.myListWidget11.addItem(item)

            self.dockwidget.myListWidget11.itemSelectionChanged.connect(
                self.LW11_itemSelectionChanged
            )
            self.dockwidget.myListWidget12.itemSelectionChanged.connect(
                self.LW12_itemSelectionChanged
            )  ## NEEDS REFACTORING to itemSelectionChanged
            self.dockwidget.myComboBox11.currentIndexChanged.connect(self._cb11_changed)
            self.dockwidget.myListWidget12.setSelectionMode(
                QAbstractItemView.ExtendedSelection
            )
            self.dockwidget.myListWidget13.setSelectionMode(
                QAbstractItemView.ExtendedSelection
            )
            self.dockwidget.myListWidget13.hide()

            # Set Tab 2
            self.dockwidget.myTabWidget.setTabText(1, self.tr("GSI Tiles"))
            self.dockwidget.myPushButton25.setText(self.tr("Add to Map"))
            self.dockwidget.myPushButton25.setToolTip(
                self.tr("Add GSI xyz tile server to Map on QGIS")
            )
            self.dockwidget.myPushButton25.clicked.connect(self.addTile)
            for row in self._GSI:
                self.dockwidget.myListWidget23.addItem(row["name_j"])

            # Set Tab 3
            self.dockwidget.myTabWidget.setTabText(2, self.tr("Census"))
            self.dockwidget.myLabel31.setText(self.tr("Year"))
            self._tab3_set_year(2000)
            self.dockwidget.myComboBox31.setToolTip(
                self.tr(
                    "Nieghbourhood: Since 2000<br />5th Mesh: Since2005<br />Others: Since 1995"
                )
            )
            self.dockwidget.myComboBox32.addItem(self.tr("Neighbourhood"))
            self.dockwidget.myComboBox32.addItem(self.tr("3rd Mesh"))
            self.dockwidget.myComboBox32.addItem(self.tr("4th Mesh"))
            self.dockwidget.myComboBox32.addItem(self.tr("5th Mesh"))
            self.dockwidget.myComboBox32.setToolTip(
                self.tr(
                    "Nieghbourhood: population of cho, aza, etc.<br />3rd Mesh: 1 km mesh<br />4th Mesh: 500 m mesh<br />5th Mesh: 250 m mesh"
                )
            )
            self.dockwidget.myComboBox32.currentIndexChanged.connect(
                self._tab3_set_mesh
            )
            self.dockwidget.myPushButton31.setText(self.tr("Download"))
            self.dockwidget.myPushButton31.setToolTip(
                self.tr("Download census data by city")
            )
            self.dockwidget.myPushButton31.clicked.connect(self.tab3DownloadAll2)
            self.dockwidget.myPushButton32.setText(self.tr("Add to Map"))
            self.dockwidget.myPushButton32.setToolTip(
                self.tr("Add Shapefile as a Layer to Map on QGIS")
            )
            self.dockwidget.myPushButton32.clicked.connect(self.tab3AddMap)
            self.dockwidget.myListWidget31.currentItemChanged.connect(
                self._LW31_currentItemChanged
            )  ## NEEDS REFACTORING to itemSelectionChanged
            self.dockwidget.myListWidget32.itemSelectionChanged.connect(
                self._LW32_itemSelectionChanged
            )
            self.dockwidget.myListWidget32.setSelectionMode(
                QAbstractItemView.ExtendedSelection
            )
            self.dockwidget.myListWidget33.hide()
            self.dockwidget.myListWidget33.setSelectionMode(
                QAbstractItemView.ExtendedSelection
            )

            for code in range(1, 48):
                self.dockwidget.myListWidget31.addItem(
                    jpDataUtils.getPrefNameByCode(code)
                )

            # Set Tab 4
            self.dockwidget.myTabWidget.setTabText(3, self.tr("Address"))
            jpDataAddr.set_cb_prefs(self.dockwidget.myCB_Addr_1)
            self.dockwidget.myCB_Addr_1.currentIndexChanged.connect(
                self._myCB_Addr_1_changed
            )
            self.dockwidget.myCB_Addr_2.currentIndexChanged.connect(
                lambda: jpDataAddr.set_cb_towns(
                    self.dockwidget.myCB_Addr_3,
                    self._folderPath,
                    self.dockwidget.myCB_Addr_1.currentText(),
                    self.dockwidget.myCB_Addr_2.currentText(),
                )
            )
            self.dockwidget.myCB_Addr_3.currentIndexChanged.connect(
                lambda: jpDataAddr.set_cb_details(
                    self.dockwidget.myCB_Addr_4,
                    self._folderPath,
                    self.dockwidget.myCB_Addr_1.currentText(),
                    self.dockwidget.myCB_Addr_2.currentText(),
                    self.dockwidget.myCB_Addr_3.currentText(),
                )
            )
            if os.path.exists(
                os.path.join(self._folderPath, "Addr")
            ):  # If the folder exists, set cities
                self.dockwidget.myPB_Addr_1.setText(self.tr("Jump"))
            else:
                self.dockwidget.myPB_Addr_1.setText(self.tr("Download"))
            self.dockwidget.myPB_Addr_1.clicked.connect(self.myPB_Addr_1_clicked)

            # Tab Setting
            self.dockwidget.myTabWidget.setTabText(4, self.tr("Setting"))
            self.dockwidget.myLineEditSetting1.textEdited.connect(self.setProxyServer)
            self.dockwidget.myLineEditSetting2.textEdited.connect(self.setProxyServer)
            self.dockwidget.myLineEditSetting3.textEdited.connect(self.setProxyServer)
            if self._folderPath:
                self.dockwidget.myLabel1.setText(self._folderPath)
            else:
                self.dockwidget.myLabel1.setText(
                    self.tr("Choose folder for zip/shp files")
                )
                self.dockwidget.myTabWidget.setCurrentIndex(3)
            if self._proxyServer:
                self.dockwidget.myLineEditSetting1.setText(self._proxyServer)
            self.dockwidget.myLineEditSetting2.setToolTip(
                self.tr("User/Password are not stored")
            )
            self.dockwidget.myLineEditSetting3.setEchoMode(QLineEdit.Password)
            self.dockwidget.myLineEditSetting3.setToolTip(
                self.tr("User/Password are not stored")
            )
            self.dockwidget.myCheckBox1.setText(self.tr("Turn off background download"))
            self.dockwidget.myCheckBox1.stateChanged.connect(
                self.set_background_download
            )
            self.dockwidget.myCheckBox2.setText(
                self.tr("Check geometry validity when adding")
            )
            self.dockwidget.myCheckBox2.stateChanged.connect(
                self.set_geometrycheck_option
            )
            if QSettings().value("jpdata/CheckGeometry", "true") == "false":
                self.dockwidget.myCheckBox2.setChecked(False)
            else:
                self.dockwidget.myCheckBox2.setChecked(True)

            if self._verbose:
                self.dockwidget.myPushButtonTest.clicked.connect(self._test_verbose)
            else:
                self.dockwidget.myPushButtonTest.hide()

            if self._folderPath == "~":
                self.dockwidget.myTabWidget.setCurrentIndex(4)
                self.setLabel(self.tr("Choose folder for zip/shp files"))
            else:
                self.dockwidget.myTabWidget.setCurrentIndex(0)

            self.dockwidget.myTabWidget.currentChanged.connect(self._tab_changed)

            # show the dockwidget
            # TODO: fix to allow choice of dock location
            self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dockwidget)
            self.dockwidget.show()

    def addTile(self):
        selected_items = self.dockwidget.myListWidget23.selectedItems()

        for current_gsi in self._GSI:
            if current_gsi["name_j"] == str(selected_items[0].text()):
                tile_name = current_gsi["name_j"]
                tile_url = current_gsi["url"]
                zoom_min = current_gsi["zoom_min"]
                zoom_max = current_gsi["zoom_max"]
                break

        tile_url = (
            "type=xyz&url="
            + tile_url
            + "&zmax="
            + zoom_max
            + "&zmin="
            + zoom_min
            + "&crs=EPSG3857"
        )
        layer = QgsRasterLayer(tile_url, tile_name, "wms")
        if not layer.isValid():
            return
        QgsProject.instance().addMapLayer(layer)

    def enable_download(self, enable=True):
        if enable:
            self.dockwidget.myPushButton11.setText(self.tr("Download"))
            self.dockwidget.myPushButton31.setText(self.tr("Download"))
            self.dockwidget.myPushButton14.setEnabled(True)
            self.dockwidget.myPushButton32.setEnabled(True)
        else:
            self.dockwidget.myPushButton11.setText(self.tr("Cancel"))
            self.dockwidget.myPushButton31.setText(self.tr("Cancel"))
            self.dockwidget.myPushButton14.setEnabled(False)
            self.dockwidget.myPushButton32.setEnabled(False)

    def LW11_itemSelectionChanged(self):
        if len(self.dockwidget.myListWidget11.selectedItems()) == 0:
            return
        name_map = self.dockwidget.myListWidget11.selectedItems()[0].text()

        prevLandNum = self._LandNumInfo2.get(self.name_map_prev, {})
        thisLandNum = self._LandNumInfo2[name_map]
        self.setLabel(thisLandNum.get("code_map", ""))

        str_current_LW12_selected = [
            item.text() for item in self.dockwidget.myListWidget12.selectedItems()
        ]
        str_new_LW12_text = []
        bol_redraw_LW12 = True
        bol_show_LW13 = False

        muni_type = thisLandNum.get("type_muni", "").lower()

        def all_prefs():
            return [jpDataUtils.getPrefNameByCode(code) for code in range(1, 48)]

        if muni_type in ("", "allprefs"):
            if not self.name_map_prev:
                str_new_LW12_text = all_prefs()
            elif prevLandNum.get("type_muni", "").lower() in ("", "allprefs", "mesh1"):
                bol_redraw_LW12 = False
                self.dockwidget.myListWidget13.clear()
                self.dockwidget.myListWidget13.hide()
            else:
                str_new_LW12_text = all_prefs()

        elif muni_type == "single":
            if prevLandNum.get("type_muni", "").lower() == "single":
                bol_redraw_LW12 = False
            else:
                str_new_LW12_text = [self.tr("Nation-wide")]
                str_current_LW12_selected = [self.tr("Nation-wide")]

        elif muni_type in ("regional", "detail"):
            if muni_type == "detail":
                bol_show_LW13 = True
            str_new_LW12_text = jpDataLNI.getPrefsOrRegionsByMapCode(
                thisLandNum["code_map"], thisLandNum["year"]
            )

        elif muni_type == "mesh1":
            bol_show_LW13 = True
            if self.name_map_prev != "" and prevLandNum.get(
                "type_muni", ""
            ).lower() in ("", "allprefs", "mesh1"):
                bol_redraw_LW12 = False
            else:
                str_new_LW12_text = all_prefs()

        else:
            self.setLabel(
                self.tr(
                    "jpdata.tab1CheckPrefsOrRegions: Unexpected type_muni in CSV: "
                    + muni_type
                )
            )
            return

        if bol_redraw_LW12:
            self._tab1_clear(bol_show_LW13)
            for new_text in str_new_LW12_text:
                item = QListWidgetItem(new_text)
                self.dockwidget.myListWidget12.addItem(item)
                if new_text in str_current_LW12_selected:
                    item.setSelected(True)

        self._tab1_check_year(name_map)
        self.name_map_prev = name_map

    def _tab1_clear(self, bol_show_LW13):
        self.dockwidget.myListWidget12.clear()
        if bol_show_LW13:
            self.dockwidget.myListWidget12.setSelectionMode(
                QAbstractItemView.SingleSelection
            )
            self.dockwidget.myListWidget13.show()
        else:
            self.dockwidget.myListWidget12.setSelectionMode(
                QAbstractItemView.ExtendedSelection
            )
            self.dockwidget.myListWidget13.clear()
            self.dockwidget.myListWidget13.hide()

    def LW12_itemSelectionChanged(self):
        if len(self.dockwidget.myListWidget11.selectedItems()) == 0:
            # header or inactive map selected
            return
        if len(self.dockwidget.myListWidget12.selectedItems()) == 0:
            return
        name_map = self.dockwidget.myListWidget11.selectedItems()[0].text()
        name_pref = self.dockwidget.myListWidget12.selectedItems()[0].text()
        self._tab1_check_year(name_map)
        thisLandNum = self._LandNumInfo2[name_map]
        if (
            thisLandNum["type_muni"].lower() == "detail"
            or thisLandNum["type_muni"].lower() == "mesh1"
        ):
            self._tab1_set_LW13(name_pref)

    def _tab1_check_year(self, name_map=None):
        thisLandNum = self._LandNumInfo2[name_map]
        name_pref = None
        str_current_year = self.dockwidget.myComboBox11.currentText()

        self.dockwidget.myComboBox11.clear()
        if thisLandNum["year"].upper()[-3:] != "CSV":
            self.dockwidget.myComboBox11.addItem(thisLandNum["year"])
        else:
            if len(self.dockwidget.myListWidget12.selectedItems()) > 0:
                if thisLandNum["type_muni"].lower() != "mesh1":
                    name_pref = self.dockwidget.myListWidget12.selectedItems()[0].text()
            years = jpDataLNI.getYearsByMapCode(
                thisLandNum["code_map"], name_pref, thisLandNum["year"]
            )
            for year in years:
                if year != "":
                    self.dockwidget.myComboBox11.addItem(year)

        if str_current_year != "":
            index = self.dockwidget.myComboBox11.findText(str_current_year)
            if index != -1:
                self.dockwidget.myComboBox11.setCurrentIndex(index)

        self._tab1_set_LW13()

    def _cb11_changed(self, current):
        if current is None:
            return
        self._tab1_set_LW13()

    # When _LW12_changed, the selected item is not set yet.
    # So, name_pref is passed from _LW12_changed.
    def _tab1_set_LW13(self, name_pref=None):
        if len(self.dockwidget.myListWidget11.selectedItems()) == 0:
            return
        if name_pref is None:
            if len(self.dockwidget.myListWidget12.selectedItems()) == 0:
                return
            else:
                name_pref = str(
                    self.dockwidget.myListWidget12.selectedItems()[0].text()
                )
        if str(self.dockwidget.myComboBox11.currentText()).strip() == "":
            return

        str_name_j = str(self.dockwidget.myListWidget11.selectedItems()[0].text())
        str_name_pref = name_pref
        str_year = str(self.dockwidget.myComboBox11.currentText())

        thisLandNum = self._LandNumInfo2[str_name_j]
        if (
            thisLandNum["type_muni"].lower() != "detail"
            and thisLandNum["type_muni"].lower() != "mesh1"
        ):
            return
        map_code = thisLandNum["code_map"]

        str_current_text = []
        if len(self.dockwidget.myListWidget13.selectedItems()) > 0:
            for item in self.dockwidget.myListWidget13.selectedItems():
                str_current_text.append(str(item.text()))

        self.dockwidget.myListWidget13.clear()
        self.dockwidget.myListWidget13.show()

        if thisLandNum["type_muni"].lower() == "detail":
            details = jpDataLNI.getDetailsByMapCodePrefNameYear(
                map_code, str_name_pref, str_year
            )
        else:
            details = jpDataMesh.getMesh1ByPrefName(str_name_pref)

        for detail in details:
            item = QListWidgetItem(detail)
            self.dockwidget.myListWidget13.addItem(item)
            if detail in str_current_text:
                item.setSelected(True)

    def tab1CheckSelected(self):
        if len(self.dockwidget.myListWidget11.selectedItems()) == 0:
            self.setLabel(self.tr("Please choose a map."))
            return False
        if len(self.dockwidget.myListWidget12.selectedItems()) == 0:
            self.setLabel(self.tr("Please choose a prefecture or region."))
            return False

        thisLandNum = self._LandNumInfo2[
            str(self.dockwidget.myListWidget11.selectedItems()[0].text())
        ]
        if (
            thisLandNum["type_muni"].lower() == "detail"
            and len(self.dockwidget.myListWidget13.selectedItems()) == 0
        ):
            self.setLabel(self.tr("Please choose one."))
            return False
        if (
            thisLandNum["type_muni"].lower() == "mesh1"
            and len(self.dockwidget.myListWidget13.selectedItems()) == 0
        ):
            self.setLabel(self.tr("Please choose a mesh."))
            return False

        return True

    def tab1DownloadAll(self):
        if not self.tab1CheckSelected():
            return
        if self.dockwidget.myPushButton11.text() == self.tr("Cancel"):
            self.cancel_download()
            return

        self._dl_url_zip = []
        self._dl_iter = 0
        year = str(self.dockwidget.myComboBox11.currentText())
        pref_names = self.dockwidget.myListWidget12.selectedItems()

        thisLandNum = self._LandNumInfo2[
            str(self.dockwidget.myListWidget11.selectedItems()[0].text())
        ]

        for pref_name in pref_names:
            if thisLandNum["type_muni"].lower() == "mesh1":
                str_replace_after = str(
                    self.dockwidget.myListWidget13.selectedItems()[0].text()
                )
            else:
                str_replace_after = jpDataUtils.getPrefCodeByName(str(pref_name.text()))

            tempUrl, tempZip, tempSubFolder = jpDataLNI.getZip(
                year, thisLandNum, str(pref_name.text()), str_replace_after, "urlzip"
            )
            if tempZip is not None:
                self._dl_url_zip.append(
                    {
                        "year": year,
                        "url": tempUrl,
                        "zip": tempZip,
                        "subfolder": tempSubFolder,
                    }
                )
        self._download_iter_2()

    def tab1Web(self):
        items = self.dockwidget.myListWidget11.selectedItems()
        for i in range(len(items)):
            thisLandNum = self._LandNumInfo2[str(items[i].text())]
            url = QUrl(thisLandNum["source"])
            QDesktopServices.openUrl(url)
            break

    def tab1AddMap(self):
        if not self.tab1CheckSelected():
            return
        thisLandNum = self._LandNumInfo2[
            str(self.dockwidget.myListWidget11.selectedItems()[0].text())
        ]
        year = str(self.dockwidget.myComboBox11.currentText())
        pref_names = self.dockwidget.myListWidget12.selectedItems()
        pref_code = []

        if thisLandNum["type_muni"].lower() == "mesh1":
            for code_mesh1 in self.dockwidget.myListWidget13.selectedItems():
                pref_code.append(str(code_mesh1.text()))
        else:
            for pref_name in pref_names:
                pref_code.append(jpDataUtils.getPrefCodeByName(str(pref_name.text())))

        if (
            thisLandNum["type_muni"].lower() == "detail"
            and len(self.dockwidget.myListWidget13.selectedItems()) > 0
        ):
            detail = str(self.dockwidget.myListWidget13.selectedItems()[0].text())
        else:
            detail = None
        if thisLandNum["type_muni"] == "single":
            pref_code = [""]

        for x in range(len(pref_code)):
            (
                zip_filename,
                shp_filename,
                altdir,
                qml_file,
                epsg,
                encoding,
                subfolder,
                layer_name,
            ) = jpDataLNI.getZip(
                year,
                thisLandNum,
                str(pref_names[x].text()),
                pref_code[x],
                "full",
                detail=detail,
            )

            shp_full_path = jpDataUtils.unzipAndGetShp(
                posixpath.join(self._folderPath, subfolder),
                zip_filename,
                shp_filename,
                altdir,
                pref_code[x],
                epsg=epsg,
                encoding=encoding,
            )

            if shp_full_path is None:
                self.setLabel(self.tr("Cannot find the .shp file: ") + shp_filename)
                self.iface.messageBar().pushMessage(
                    "Error",
                    "Cannot find the .shp file: " + shp_filename,
                    1,
                    duration=10,
                )
                break

            if shp_full_path != "":
                self._add_map(shp_full_path, layer_name, qml_file, encoding)

    def start_download(self, url, subFolder, zipFileName):
        if not os.path.exists(posixpath.join(self._folderPath, subFolder)):
            os.mkdir(posixpath.join(self._folderPath, subFolder))

        if not os.path.exists(posixpath.join(self._folderPath, subFolder, zipFileName)):
            self.setProxyServer()
            self.setLabel(self.tr("Downloading: ") + zipFileName)
            self._downloader.setUrl(url)
            self._downloader.setFilePath(
                posixpath.join(self._folderPath, subFolder, zipFileName)
            )
            self.enable_download(False)
            if self.dockwidget.myCheckBox1.isChecked():
                self._downloader.download_wo_thread()
                self.enable_download()
            else:
                self._downloader.start()
        else:
            self.setLabel(self.tr("The zip file exists: ") + zipFileName)
            self.enable_download()

    def download_finished(self, success):
        current_text = self.dockwidget.myLabelStatus.text()
        self.setLabel(current_text + self.tr("...Done"))
        self.enable_download()
        self.dockwidget.progressBar.setValue(100)

        if len(self._dl_url_zip) > 0 and self._dl_iter < len(self._dl_url_zip):
            # Download next
            self._download_iter_2()
        else:
            # All downloads finished
            self._dl_iter = 0
            self._dl_url_zip = []
            if self._dl_status == "ADDRESS":
                self.dockwidget.myPB_Addr_1.setText(self.tr("Jump"))
                self._myCB_Addr_1_changed()

    def cancel_download(self):
        self._dl_url_zip = []
        if self._downloader is not None:
            current_text = self.dockwidget.myLabelStatus.text()
            self.setLabel(current_text + self.tr("...Cancelled"))
            self._downloader.stop()
        else:
            self._downloader = jpDataDownloader.DownloadThread()
        self.enable_download()

    def _LW31_currentItemChanged(self, current, previous):
        if current is None or current == previous:
            return
        if isinstance(current, int):
            return
        name_pref = current.text()
        rows = jpDataMuni.getMuniFromPrefName(name_pref)
        self.dockwidget.myListWidget32.clear()
        for row in rows:
            if row["name_muni"] != "":
                item = QListWidgetItem(row["name_muni"])
                if row["name_muni"] in [
                    "札幌市",
                    "仙台市",
                    "千葉市",
                    "さいたま市",
                    "横浜市",
                    "川崎市",
                    "相模原市",
                    "新潟市",
                    "静岡市",
                    "浜松市",
                    "名古屋市",
                    "京都市",
                    "大阪市",
                    "堺市",
                    "神戸市",
                    "岡山市",
                    "広島市",
                    "福岡市",
                    "北九州市",
                    "熊本市",
                ]:
                    item.setFlags(item.flags() & ~Qt.ItemIsSelectable)
                    item.setForeground(Qt.gray)
                self.dockwidget.myListWidget32.addItem(item)
        self._tab3_set_mesh()

    def _tab3_set_year(self, firstYear):
        currentYear = str(self.dockwidget.myComboBox31.currentText())
        if firstYear == 2005:
            years = ["2020", "2015", "2010", "2005"]
        if firstYear == 2000:
            years = ["2020", "2015", "2010", "2005", "2000"]
        elif firstYear == 1995:
            years = ["2020", "2015", "2010", "2005", "2000", "1995"]
        self.dockwidget.myComboBox31.clear()
        for year in years:
            self.dockwidget.myComboBox31.addItem(year)
        # Select an item programmatically
        index = self.dockwidget.myComboBox31.findText(
            currentYear
        )  # Find the index of the item
        if index != -1:  # Ensure the item exists
            self.dockwidget.myComboBox31.setCurrentIndex(index)

    def _LW32_itemSelectionChanged(self):
        self._tab3_set_mesh()

    def _tab3_set_mesh(self):
        if len(self.dockwidget.myListWidget31.selectedItems()) == 0:
            return
        name_pref = str(self.dockwidget.myListWidget31.selectedItems()[0].text())

        if self.dockwidget.myComboBox32.currentIndex() == 0:
            self.dockwidget.myListWidget33.clear()
            self.dockwidget.myListWidget33.hide()
            self._tab3_set_year(2000)
            return
        elif (
            self.dockwidget.myComboBox32.currentIndex() == 1
            or self.dockwidget.myComboBox32.currentIndex() == 2
        ):
            self._tab3_set_year(1995)
        else:
            self._tab3_set_year(2005)

        self.dockwidget.myListWidget33.clear()
        self.dockwidget.myListWidget33.show()

        if len(self.dockwidget.myListWidget32.selectedItems()) == 0:
            details = jpDataMesh.getMesh1ByPrefName(name_pref)
        else:
            name_munis = []
            for name_muni in self.dockwidget.myListWidget32.selectedItems():
                name_munis.append(name_muni.text())
            jpDataUtils.printLog(name_munis)
            details = jpDataMesh.getMesh1ByPrefMuniName(name_pref, name_munis)

        for detail in details:
            self.dockwidget.myListWidget33.addItem(detail)

    def tab3CheckSelected(self):
        if len(self.dockwidget.myListWidget31.selectedItems()) == 0:
            self.setLabel(self.tr("Please choose a prefecture."))
            return False
        if self.dockwidget.myComboBox32.currentIndex() == 0:
            if len(self.dockwidget.myListWidget32.selectedItems()) == 0:
                self.setLabel(self.tr("Please choose a municipality."))
                return False
        else:
            if len(self.dockwidget.myListWidget33.selectedItems()) == 0:
                self.setLabel(self.tr("Please choose a mesh code."))
                return False
        return True

    def tab3DownloadAll2(self):
        if self.dockwidget.myPushButton31.text() == self.tr("Cancel"):
            self.cancel_download()
            return
        if not self.tab3CheckSelected():
            return

        self._dl_url_zip = []
        self._dl_iter = 0
        year = str(self.dockwidget.myComboBox31.currentText())
        name_pref = self.dockwidget.myListWidget31.selectedItems()[0].text()
        code_pref = jpDataUtils.getPrefCodeByName(name_pref)
        # Check if municipality (shochiiki) is selected
        if self.dockwidget.myComboBox32.currentIndex() == 0:
            # Get municipality names
            muni_names = self.dockwidget.myListWidget32.selectedItems()
        else:
            # Get mesh codes
            muni_names = self.dockwidget.myListWidget33.selectedItems()
        for muni_name in muni_names:
            # Usually, attributes are in one file, so for loop is not
            # really necessary
            row = jpDataMuni.getRowFromNames(name_pref, str(muni_name.text()))
            if self.dockwidget.myComboBox32.currentIndex() == 0:
                code_muni = row["code_muni"]
            else:
                code_muni = str(muni_name.text())

            # Append the attribute data first
            tempUrl, tempZip, tempSubFolder = jpDataCensus.getAttr(
                year,
                code_pref,
                code_muni,
                self.dockwidget.myComboBox32.currentIndex(),
            )
            if tempZip is not None:
                self._dl_url_zip.append(
                    {
                        "year": year,
                        "url": tempUrl,
                        "zip": tempZip,
                        "subfolder": tempSubFolder,
                    }
                )
            # Append the shp data
            tempUrl, tempZip, tempSubFolder = jpDataCensus.getZip(
                year,
                code_pref,
                code_muni,
                self.dockwidget.myComboBox32.currentIndex(),
            )
            if tempZip is not None:
                self._dl_url_zip.append(
                    {
                        "year": year,
                        "url": tempUrl,
                        "zip": tempZip,
                        "subfolder": tempSubFolder,
                    }
                )

        self._download_iter_2()

    def tab3AddMap(self):
        if not self.tab3CheckSelected():
            return
        year = str(self.dockwidget.myComboBox31.currentText())
        name_pref = self.dockwidget.myListWidget31.selectedItems()[0].text()
        code_pref = jpDataUtils.getPrefCodeByName(name_pref)
        tempSubFolder = jpDataCensus.getSubFolder(
            self.dockwidget.myComboBox32.currentIndex()
        )

        if self.dockwidget.myComboBox32.currentIndex() == 0:
            muni_names = self.dockwidget.myListWidget32.selectedItems()
            tempQmlFile = "Census-" + year + ".qml"
            name_muni_suffix = ""
        else:
            muni_names = self.dockwidget.myListWidget33.selectedItems()
            if self.dockwidget.myComboBox32.currentIndex() == 1:
                tempQmlFile = "Census-SDDSWS-" + year + ".qml"
                name_muni_suffix = " " + self.tr("3rd")
            elif self.dockwidget.myComboBox32.currentIndex() == 2:
                tempQmlFile = "Census-HDDSWH-" + year + ".qml"
                name_muni_suffix = " " + self.tr("4th")
            elif self.dockwidget.myComboBox32.currentIndex() == 3:
                tempQmlFile = "Census-QDDSWQ-" + year + ".qml"
                name_muni_suffix = " " + self.tr("5th")

        for muni_name in muni_names:
            name_muni = str(muni_name.text())
            if self.dockwidget.myComboBox32.currentIndex() == 0:
                row = jpDataMuni.getRowFromNames(name_pref, name_muni)
                code_muni = row["code_muni"]
            else:
                code_muni = name_muni
            tempZipFileName, tempShpFileName = jpDataCensus.getZipShp(
                year,
                code_pref,
                code_muni,
                self.dockwidget.myComboBox32.currentIndex(),
            )

            tempShpFullPath = jpDataUtils.unzipAndGetShp(
                posixpath.join(self._folderPath, tempSubFolder),
                tempZipFileName,
                tempShpFileName,
            )

            if tempShpFullPath is None:
                self.setLabel(self.tr("Cannot find the .shp file: ") + tempShpFileName)
                self.iface.messageBar().pushMessage(
                    "Error",
                    "Cannot find the .shp file: " + tempShpFileName,
                    1,
                    duration=10,
                )
                break

            if tempShpFullPath != "":
                tempCsvFileName = jpDataCensus.getAttrCsvFileName(
                    year,
                    code_pref,
                    code_muni,
                    self.dockwidget.myComboBox32.currentIndex(),
                )
                tempUrl, tempZip, tempSubFolder = jpDataCensus.getAttr(
                    year,
                    code_pref,
                    code_muni,
                    self.dockwidget.myComboBox32.currentIndex(),
                )
                jpDataUtils.unzip(
                    posixpath.join(self._folderPath, tempSubFolder), tempZip
                )
                tempShpFullPath, encoding = jpDataCensus.performJoin(
                    posixpath.join(self._folderPath, tempSubFolder),
                    year,
                    tempShpFileName,
                    tempCsvFileName,
                )
                self._add_map(
                    tempShpFullPath,
                    name_muni + name_muni_suffix + " (" + year + ")",
                    tempQmlFile,
                    encoding=encoding,
                )

    def _add_map(self, shpFileFullPath, layerName, qmlFileName, encoding="CP932"):
        tempLayer = QgsVectorLayer(shpFileFullPath, layerName, "ogr")
        tempLayer.setProviderEncoding(encoding)
        # if not os.path.exists(shpFileFullPath[:-4] + ".qix"):
        #     tempLayer.dataProvider().createSpatialIndex()
        count_invalid_geom = self.count_invalid_geometry(tempLayer)
        if count_invalid_geom > 0:
            tempLayer.setName(layerName + " [invalid]")
            self.setLabel(
                self.tr("The layer has invalid geometries: ") + str(count_invalid_geom)
            )

        if os.path.isfile(posixpath.join(self.plugin_dir, "qml", qmlFileName)):
            # For the qml files that use SVG images in the plugin folder
            with tempfile.TemporaryDirectory() as temp_dir:
                with open(
                    posixpath.join(self.plugin_dir, "qml", qmlFileName),
                    "r",
                ) as file:
                    file_contents = file.read()
                new_contents = file_contents.replace("PLUGIN_DIR", self.plugin_dir)
                with open(posixpath.join(temp_dir, qmlFileName), "w") as file:
                    file.write(new_contents)
                if tempLayer.loadNamedStyle(posixpath.join(temp_dir, qmlFileName)):
                    tempLayer.triggerRepaint()
        QgsProject.instance().addMapLayer(tempLayer)
        return tempLayer

    def chooseFolder(self):
        # Open a folder dialog to choose a folder
        self._folderPath = QFileDialog.getExistingDirectory(
            self.iface.mainWindow(), self.tr("Choose Folder")
        )
        if not os.access(self._folderPath, os.W_OK):
            self.setLabel(self.tr("The folder is not writable."))

        if self._folderPath:
            self.enable_download()
            self.dockwidget.myLabel1.setText(self._folderPath)
            QgsSettings().setValue("jpdata/FolderPath", self._folderPath)

    def setProxyServer(self):
        _proxyServer = self.dockwidget.myLineEditSetting1.text()
        if len(_proxyServer) > 10:
            if self._proxyServer != _proxyServer:
                self._proxyServer = _proxyServer
                QgsSettings().setValue("jpdata/ProxyServer", self._proxyServer)
                self._downloader.setProxyServer(self._proxyServer)
            self._downloader.setProxyUser(self.dockwidget.myLineEditSetting2.text())
            self._downloader.setProxyPassword(self.dockwidget.myLineEditSetting3.text())
        else:
            self._downloader.setProxyServer("")
            QgsSettings().setValue("jpdata/ProxyServer", "http://")
            self._proxyServer = "http://"

    # year = 2023 and so on
    # type must be one of ["regional","detail","single","","census"]
    # selection_code is a dictionary of codes
    #     (prefectural codes, municipal codes and so on)
    # a list of "code_pref"s (type = "" or "region")
    # or a list of "code_muni"s (type = "census")
    def _download_iter_2(self):
        _start_download = False

        for x in range(self._dl_iter, len(self._dl_url_zip)):
            self.setLabel("Fetching " + str(x) + " in " + str(len(self._dl_url_zip)))
            tempUrl = self._dl_url_zip[x]["url"]
            tempZipFileName = self._dl_url_zip[x]["zip"]
            tempSubFolder = self._dl_url_zip[x]["subfolder"]
            self.setLabel(self._folderPath)
            self.setLabel(tempZipFileName)
            if not os.path.exists(
                posixpath.join(
                    self._folderPath,
                    tempSubFolder,
                    tempZipFileName,
                )
            ):
                _start_download = True
                break
            else:
                self.setLabel(self.tr("The zip file exists: ") + tempZipFileName)
        if _start_download:
            self.dockwidget.progressBar.setValue(0)
            self.enable_download(False)
            self._dl_iter = x + 1
            self.start_download(tempUrl, tempSubFolder, tempZipFileName)
        else:
            self.enable_download()
            self._dl_iter = 0

    def setLabel(self, message):
        self.dockwidget.myLabelStatus.setText(message)
        if self._verbose:
            jpDataUtils.printLog(message)

    def set_background_download(self):
        """Set the background download option."""
        if self.dockwidget.myCheckBox1.isChecked():
            self.dockwidget.progressBar.enabled = False
        else:
            self.dockwidget.progressBar.enabled = True

    def count_invalid_geometry(self, layer):
        """Check the geometry validity of a given vector layer."""
        if self.dockwidget.myCheckBox2.isChecked() == False:
            return 0
        if isinstance(layer, QgsVectorLayer):
            count = 0
            for feature in layer.getFeatures():
                if not feature.geometry().isGeosValid():
                    count = count + 1
        return count

    def set_geometrycheck_option(self):
        """Set the geometry check option."""
        if self.dockwidget.myCheckBox2.isChecked():
            QgsSettings().setValue("jpdata/CheckGeometry", "true")
        else:
            QgsSettings().setValue("jpdata/CheckGeometry", "false")

    def myPB_Addr_1_clicked(self):
        if self.dockwidget.myPB_Addr_1.text() == self.tr("Download"):
            self._dl_url_zip = []
            self._dl_iter = 0
            for i in range(1, 48):
                if not os.path.exists(
                    posixpath.join(
                        self._folderPath,
                        "Addr",
                        jpDataAddr.get_zip(i),
                    )
                ) and not jpDataAddr.get_csv_fullpath(i, self._folderPath):
                    self._dl_url_zip.append(
                        {
                            "year": "2024",
                            "url": jpDataAddr.get_url(i),
                            "zip": jpDataAddr.get_zip(i),
                            "subfolder": "Addr",
                        }
                    )
            if len(self._dl_url_zip) > 0:
                self._download_iter_2()
        elif self.dockwidget.myPB_Addr_1.text() == self.tr("Jump"):
            lon, lat = jpDataAddr.get_lonlat_by_addr(
                self._folderPath,
                str(self.dockwidget.myCB_Addr_1.currentText()),
                str(self.dockwidget.myCB_Addr_2.currentText()),
                str(self.dockwidget.myCB_Addr_3.currentText()),
                str(self.dockwidget.myCB_Addr_4.currentText()),
            )

            if lon is None or lat is None:
                return

            point_jgd2011 = QgsPointXY(lon, lat)

            # Transform to project CRS
            crs_src = QgsCoordinateReferenceSystem("EPSG:6668")  # JGD2011
            crs_dest = iface.mapCanvas().mapSettings().destinationCrs()
            transform = QgsCoordinateTransform(crs_src, crs_dest, QgsProject.instance())
            point_project = transform.transform(point_jgd2011)

            # Set canvas center
            canvas = iface.mapCanvas()
            canvas.setCenter(point_project)
            canvas.refresh()

    def _myCB_Addr_1_changed(self):
        if not jpDataAddr.set_cb_cities(
            self.dockwidget.myCB_Addr_2,
            self._folderPath,
            self.dockwidget.myCB_Addr_1.currentText(),
        ):
            self.dockwidget.myPB_Addr_1.setText(self.tr("Download"))
            self.setLabel(self.tr("Missing address data."))
            self._dl_status = "ADDRESS"
        else:
            self.dockwidget.myPB_Addr_1.setText(self.tr("Jump"))

    def _tab_changed(self, index):
        """Called whenever the current tab changes."""
        if index == 3:  # tab #3 (4th tab)
            self.dockwidget.myCB_Addr_1.setCurrentIndex(12)

    def _test_verbose(self):
        self.setLabel(self._downloader.getProxyServer())
