"""
Script creates a project with the MVC pattern
=============================================

.. versionadded:: 1.0.0

.. seealso::

    `MVC pattern <https://en.wikipedia.org/wiki/Model–view–controller>`_

.. rubric:: Use a clean architecture for your applications.

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/preview-mvc.png
    :align: center

Use a clean architecture for your applications. KivyMD allows you to quickly
create a project template with the MVC pattern. So far, this is the only
pattern that this utility offers. You can also include database support in
your project. At the moment, support for the Firebase database
(the basic implementation of the real time database) and RestDB
(the full implementation) is available.

Project creation
----------------

Template command::

    kivymd.create_project \\
        name_pattern \\
        path_to_project \\
        name_project \\
        python_version \\
        kivy_version

Example command::

    kivymd.create_project \\
        MVC \\
        /Users/macbookair/Projects \\
        MyMVCProject \\
        python3.10 \\
        2.1.0

This command will by default create a project with an MVC pattern.
Also, the project will create a virtual environment with Python 3.10,
Kivy version 2.1.0 and KivyMD master version.

.. note::
    Please note that the Python version you specified must be installed on your
    computer.

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/mvc-base.png
    :align: center

Creating a project using a database
-----------------------------------

.. note::
    Note that in the following command, you can use one of two database names:
    'firebase' or 'restdb'.

Template command::

    kivymd.create_project \\
        name_pattern \\
        path_to_project \\
        name_project \\
        python_version \\
        kivy_version \\
        --name_database

Example command::

    kivymd.create_project \\
        MVC \\
        /Users/macbookair/Projects \\
        MyMVCProject \\
        python3.10 \\
        2.1.0 \\
        --name_database restdb

This command will create a project with an MVC template by default.
The project will also create a virtual environment with Python 3.10,
Kivy version 2.1.0, KivyMD master version and a wrapper for working with
the database restdb.io.

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/mvc-database.png
    :align: center

.. code-block:: python

    class DataBase:
        def __init__(self):
            database_url = "https://restdbio-5498.restdb.io"
            api_key = "7ce258d66f919d3a891d1166558765f0b4dbd"

.. note::
    Please note that `database.py` the shell in the `DataBase` class uses the
    `database_url` and `api_key` parameters on the test database (works only in read mode),
    so you should use your data for the database.

Create project with hot reload
------------------------------

Template command::

    kivymd.create_project \\
        name_pattern \\
        path_to_project \\
        name_project \\
        python_version \\
        kivy_version \\
        --use_hotreload

Example command::

    kivymd.create_project \\
        MVC \\
        /Users/macbookair/Projects \\
        MyMVCProject \\
        python3.10 \\
        2.1.0 \\
        --use_hotreload yes

After creating the project, open the file `main.py`, there is a lot of useful
information. Also, the necessary information is in other modules of the project
in the form of comments. So do not forget to look at the source files of the
created project.

Create project with responsive view
-----------------------------------

When creating a project, you can specify which views should use responsive
behavior. To do this, specify the name of the view/views in the
`--use_responsive` argument:

Template command::

    kivymd.create_project \\
        name_pattern \\
        path_to_project \\
        name_project \\
        python_version \\
        kivy_version \\
        --name_screen FirstScreen SecondScreen ThirdScreen \\
        --use_responsive FirstScreen SecondScreen

The `FirstScreen` and `SecondScreen` views will be created with an responsive
architecture. For more detailed information about using the adaptive view, see
the `MDResponsiveLayout <https://kivymd.readthedocs.io/en/latest/components/responsivelayout/>`_
widget.

Others command line arguments
=============================

Required Arguments
------------------

- pattern
    - the name of the pattern with which the project will be created

- directory
    - directory in which the project will be created

- name
    - project name

- python_version
    - the version of Python (specify as `python3.9` or `python3.8`) with
    - which the virtual environment will be created

- kivy_version
    - version of Kivy (specify as `2.1.0` or `master`) that will be used in the project

Optional arguments
------------------

- name_screen
    - the name of the class which be used when creating the project pattern

When you need to create an application template with multiple screens,
use multiple values separated by a space for the `name_screen` parameter,
for example, as shown below:

Template command::

    kivymd.create_project \\
        name_pattern \\
        path_to_project \\
        name_project \\
        python_version \\
        kivy_version \\
        --name_screen FirstScreen SecondScreen ThirdScreen

- name_database
    - provides a basic template for working with the 'firebase' library
    - or a complete implementation for working with a database 'restdb.io'

- use_hotreload
    - creates a hot reload entry point to the application

- use_localization
    - creates application localization files

- use_responsive
    - the name/names of the views to be used by the responsive UI

.. warning:: On Windows, hot reloading of Python files may not work.
    But, for example, there is no such problem in macOS. If you fix this,
    please report it to the KivyMD community.
"""

__all__ = [
    "main",
]

import os
import re
import shutil
from typing import Union

from kivy import Logger, platform

from kivymd import path as kivymd_path
from kivymd.tools.argument_parser import ArgumentParserWithHelp

temp_basemodel = '''# The model implements the observer pattern. This means that the class must
# support adding, removing, and alerting observers. In this case, the model is
# completely independent of controllers and views. It is important that all
# registered observers implement a specific method that will be called by the
# model when they are notified (in this case, it is the `model_is_changed`
# method). For this, observers must be descendants of an abstract class,
# inheriting which, the `model_is_changed` method must be overridden.


class BaseScreenModel:
    """Implements a base class for model modules."""

    _observers = []

    def add_observer(self, observer) -> None:
        self._observers.append(observer)

    def remove_observer(self, observer) -> None:
        self._observers.remove(observer)

    def notify_observers(self, name_screen: str) -> None:
        """
        Method that will be called by the observer when the model data changes.

        :param name_screen:
            name of the view for which the method should be called
            :meth:`model_is_changed`.
        """

        for observer in self._observers:
            if observer.name == name_screen:
                observer.model_is_changed()
                break
'''

temp_database_model = '''import multitasking

from Model.base_model import BaseScreenModel

multitasking.set_max_threads(10)


class {name_screen}Model(BaseScreenModel):
    """
    Implements the logic of the
    :class:`~View.{name_screen}.{module_name}.{name_screen}View` class.
    """

    def __init__(self, database):
        # Just an example of the data. Use your own values.
        self._data = None

    @property
    def data(self):
        return self._data

    @data.setter
    def data(self, value):
        self._data = value
        # We notify the View -
        # :class:`~View.{name_screen}.{module_name}.{name_screen}View` about the
        # changes that have occurred in the data model.
        self.notify_observers({notify_name_screen})

    @multitasking.task
    def check_data(self):
        """Just an example of the method. Use your own code."""

        self.data = ["example item"]
'''

temp_without_database_model = '''from Model.base_model import BaseScreenModel


class {name_screen}Model(BaseScreenModel):
    """
    Implements the logic of the
    :class:`~View.{module_name}.{name_screen}.{name_screen}View` class.
    """'''

temp_screens_imports = """# The screens dictionary contains the objects of the models and controllers
# of the screens of the application.


"""

temp_code_responsive_view = '''from kivymd.uix.responsivelayout import MDResponsiveLayout

from View.{name_screen}.components import (
    MobileScreenView,
    TabletScreenView,
    DesktopScreenView,
)
from View.base_screen import BaseScreenView


class {name_screen}View(MDResponsiveLayout, BaseScreenView):
    def __init__(self, **kw):
        super().__init__(**kw)
        self.mobile_view = MobileScreenView()
        self.tablet_view = TabletScreenView()
        self.desktop_view = DesktopScreenView()

    def model_is_changed(self) -> None:
        """
        Called whenever any change has occurred in the data model.
        The view in this method tracks these changes and updates the UI
        according to these changes.
        """
'''

temp_responsive_component_imports = """from .platforms.MobileScreen.mobile_screen import MobileScreenView
from .platforms.TabletScreen.tablet_screen import TabletScreenView
from .platforms.DesktopScreen.desktop_screen import DesktopScreenView
"""

temp_responsive_platform_baseclass = """from kivymd.uix.screen import MDScreen


class {}View(MDScreen):
    pass
"""

temp_code_view = '''from View.base_screen import BaseScreenView


class {name_screen}View(BaseScreenView):
    def model_is_changed(self) -> None:
        """
        Called whenever any change has occurred in the data model.
        The view in this method tracks these changes and updates the UI
        according to these changes.
        """
'''

temp_code_controller = '''{import_module}


class {name_screen}Controller:
    """
    The `{name_screen}Controller` class represents a controller implementation.
    Coordinates work of the view with the model.
    The controller implements the strategy pattern. The controller connects to
    the view to control its actions.
    """

    def __init__(self, model):
        self.model = model  # Model.{module_name}.{name_screen}Model
        self.view = {name_view}(controller=self, model=self.model)

    def get_view(self) -> {get_view}:
        return self.view
'''

temp_base_screen = '''from kivy.properties import ObjectProperty

from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen

from Utility.observer import Observer


class BaseScreenView(MDScreen, Observer):
    """
    A base class that implements a visual representation of the model data.
    The view class must be inherited from this class.
    """

    controller = ObjectProperty()
    """
    Controller object - :class:`~Controller.controller_screen.ClassScreenControler`.

    :attr:`controller` is an :class:`~kivy.properties.ObjectProperty`
    and defaults to `None`.
    """

    model = ObjectProperty()
    """
    Model object - :class:`~Model.model_screen.ClassScreenModel`.

    :attr:`model` is an :class:`~kivy.properties.ObjectProperty`
    and defaults to `None`.
    """

    manager_screens = ObjectProperty()
    """
    Screen manager object - :class:`~kivymd.uix.screenmanager.MDScreenManager`.

    :attr:`manager_screens` is an :class:`~kivy.properties.ObjectProperty`
    and defaults to `None`.
    """

    def __init__(self, **kw):
        super().__init__(**kw)
        # Often you need to get access to the application object from the view
        # class. You can do this using this attribute.
        self.app = MDApp.get_running_app()
        # Adding a view class as observer.
        self.model.add_observer(self)
'''

temp_utility = '''
# Of course, "very flexible Python" allows you to do without an abstract
# superclass at all or use the clever exception `NotImplementedError`. In my
# opinion, this can negatively affect the architecture of the application.
# I would like to point out that using Kivy, one could use the on-signaling
# model. In this case, when the state changes, the model will send a signal
# that can be received by all attached observers. This approach seems less
# universal - you may want to use a different library in the future.


class Observer:
    """Abstract superclass for all observers."""

    def model_is_changed(self):
        """
        The method that will be called on the observer when the model changes.
        """
'''

temp_hot_reload_main = '''
"""
Script for managing hot reloading of the project.
For more details see the documentation page -

https://kivymd.readthedocs.io/en/latest/api/kivymd/tools/patterns/create_project/

To run the application in hot boot mode, execute the command in the console:
DEBUG=1 python main.py
"""

import importlib
import os

from kivy import Config

from PIL import ImageGrab

# TODO: You may know an easier way to get the size of a computer display.
resolution = ImageGrab.grab().size

# Change the values of the application window size as you need.
Config.set("graphics", "height", resolution[1])
Config.set("graphics", "width", "400")

from kivy.core.window import Window{}

# Place the application window on the right side of the computer screen.
Window.top = 0
Window.left = resolution[0] - Window.width

from kivymd.tools.hotreload.app import MDApp
from kivymd.uix.screenmanager import MDScreenManager
{}{}

class {}(MDApp):
    KV_DIRS = [os.path.join(os.getcwd(), "View")]{}

    def build_app(self) -> MDScreenManager:
        """
        In this method, you don't need to change anything other than the
        application theme.
        """

        import View.screens

        self.manager_screens = MDScreenManager(){}{}
        Window.bind(on_key_down=self.on_keyboard_down)
        importlib.reload(View.screens)
        screens = View.screens.screens

        for i, name_screen in enumerate(screens.keys()):
            model = screens[name_screen]["model"]({})
            controller = screens[name_screen]["controller"](model)
            view = controller.get_view()
            view.manager_screens = self.manager_screens
            view.name = name_screen
            self.manager_screens.add_widget(view)

        return self.manager_screens

    def on_keyboard_down(self, window, keyboard, keycode, text, modifiers) -> None:
        """
        The method handles keyboard events.

        By default, a forced restart of an application is tied to the
        `CTRL+R` key on Windows OS and `COMMAND+R` on Mac OS.
        """

        if "meta" in modifiers or "ctrl" in modifiers and text == "r":
            self.rebuild(){}{}


{}().run()

# After you finish the project, remove the above code and uncomment the below
# code to test the application normally without hot reloading.
'''

temp_main = '''"""
The entry point to the application.

The application uses the MVC template. Adhering to the principles of clean
architecture means ensuring that your application is easy to test, maintain,
and modernize.

You can read more about this template at the links below:

https://github.com/HeaTTheatR/LoginAppMVC
https://en.wikipedia.org/wiki/Model–view–controller
"""
{}
from kivymd.app import MDApp
from kivymd.uix.screenmanager import MDScreenManager

from View.screens import screens{}
{}

class {}(MDApp):{}
    def __init__(self, **kwargs):
        super().__init__(**kwargs){}
        self.load_all_kv_files(self.directory)
        # This is the screen manager that will contain all the screens of your
        # application.
        self.manager_screens = MDScreenManager()
        {}
    def build(self) -> MDScreenManager:
        self.generate_application_screens()
        return self.manager_screens

    def generate_application_screens(self) -> None:
        """
        Creating and adding screens to the screen manager.
        You should not change this cycle unnecessarily. He is self-sufficient.

        If you need to add any screen, open the `View.screens.py` module and
        see how new screens are added according to the given application
        architecture.
        """

        for i, name_screen in enumerate(screens.keys()):
            model = screens[name_screen]["model"]({})
            controller = screens[name_screen]["controller"](model)
            view = controller.get_view()
            view.manager_screens = self.manager_screens
            view.name = name_screen
            self.manager_screens.add_widget(view)
{}{}

{}().run()
'''

temp_makefile = """# FILE TO FIND AND CREATE LOCALIZATION FILES FOR YOUR APPLICATION. \\
\\
In this file, you can specify in which files of your project to search for \\
localization strings. \\
These files should be listed in the below command: \\
\\
\\
xgettext -Lpython --output=messages.pot --from-code=utf-8 \\
    path/to/file-1 \\
    path/to/file-2 \\
    ...

.PHONY: po mo

po:
    xgettext -Lpython --output=messages.pot --from-code=utf-8 \\
{}
    msgmerge --update --no-fuzzy-matching --backup=off data/locales/po/en.po messages.pot
    msgmerge --update --no-fuzzy-matching --backup=off data/locales/po/ru.po messages.pot

mo:
    mkdir -p data/locales/en/LC_MESSAGES
    mkdir -p data/locales/ru/LC_MESSAGES
    msgfmt -c -o data/locales/en/LC_MESSAGES/%s.mo data/locales/po/en.po
    msgfmt -c -o data/locales/ru/LC_MESSAGES/%s.mo data/locales/po/ru.po
"""

firebase_requirements = """kivy==2.1.0
kivymd==1.0.0
multitasking
firebase
firebase-admin
python_jwt
gcloud
sseclient
pycryptodome==3.4.3
requests_toolbelt
"""

without_firebase_requirements = """kivy==2.1.0
kivymd==1.0.0
"""

available_patterns = ["MVC"]
available_databases = ["firebase", "restdb"]

path_to_project = ""
project_name = ""
use_localization = ""
name_database = ""
use_hotreload = ""
temp_makefile_files = ""
temp_screens_data = ""
kivy_version = ""
python_version = ""


def main():
    """Project creation function."""

    global project_name
    global use_localization
    global name_database
    global use_hotreload
    global temp_makefile_files
    global temp_screens_data
    global path_to_project
    global kivy_version
    global python_version

    parser = create_argument_parser()
    args = parser.parse_args()

    pattern_name = args.pattern
    project_directory = args.directory
    project_name = "".join(args.name.split(" "))
    kivy_version = args.kivy_version
    python_version = args.python_version
    if "3" not in python_version:
        parser.error("Python must be at least version 3")
    name_screen = args.name_screen
    path_to_project = os.path.join(project_directory, project_name)
    name_database = args.name_database
    if name_database != "no" and name_database not in available_databases:
        parser.error(
            f"The database name must be one of the {available_databases} list"
        )
    use_hotreload = args.use_hotreload
    use_localization = args.use_localization
    use_responsive = args.use_responsive

    # Check arguments.
    for name in name_screen:
        if name[-6:] != "Screen":
            parser.error(
                f"The name of the {name} screen should contain the word "
                f"'Screen' at the end.\n"
                "For example - '--name_screen MyFirstScreen ...'"
            )
    if not os.path.exists(
        os.path.join(kivymd_path, "tools", "patterns", pattern_name)
    ):
        parser.error(
            f"There is no {pattern_name} pattern.\n"
            f"Only {available_patterns} template is available."
        )

    # Call the functions of creating a project.
    if not os.path.exists(path_to_project):
        shutil.copytree(
            os.path.join(kivymd_path, "tools", "patterns", pattern_name),
            path_to_project,
        )
        create_main()

        for name in name_screen:
            module_name = chek_camel_case_name_project(name)
            if not module_name:
                parser.error(
                    "The name of the screen should be written in camel case style. "
                    "\nFor example - 'MyFirstScreen'"
                )
            module_name = "_".join([name.lower() for name in module_name])

            # Create models module.
            create_model(name, module_name, name_database, path_to_project)
            # Create controllers module.
            create_controller(name, module_name, use_hotreload, path_to_project)
            # Create screens data.
            create_screens_data(name, module_name)
            if use_localization == "yes":
                # Create makefile data.
                create_makefile_data(name, module_name)
            # Create views.
            create_view(name, module_name, use_responsive, path_to_project)

        # Create module `NameProject/View/NameScreen/components/common/__init__.py`.
        create_common_responsive_module(use_responsive, path_to_project)
        # Create module `NameProject/View/screens.py`.
        create_module_screens()
        # Create module `NameProject/Model/base_model.py`.
        create_basemodel()
        # Create module `NameProject/View/base_screen.py`.
        create_module_basescreen()
        # Create package `NameProject/Utility`.
        create_package_utility()
        # Create file `NameProject/Makefile`.
        if use_localization == "yes":
            # Create makefile data.
            create_makefile()

        create_requirements()
        os.makedirs(os.path.join(path_to_project, "assets", "images"))
        os.mkdir(os.path.join(path_to_project, "assets", "fonts"))

        if name_database != "no":
            check_databases()

        if use_hotreload == "yes":
            create_main_with_hotreload()
            with open(
                os.path.join(path_to_project, "requirements.txt"),
                "a",
                encoding="utf-8",
            ) as requirements:
                requirements.write("watchdog")

        if use_localization == "yes":
            Logger.info("KivyMD: Create localization files...")
            os.chdir(path_to_project)
            os.system("make po")
            os.system("make mo")
        else:
            os.remove(os.path.join(path_to_project, "messages.pot"))
            os.remove(os.path.join(path_to_project, "libs", "translation.py"))
            shutil.rmtree(os.path.join(path_to_project, "data"))

        Logger.info(f"KivyMD: Project '{path_to_project}' created")
        Logger.info(
            f"KivyMD: Create a virtual environment for '{path_to_project}' project..."
        )
        create_virtual_environment()
        Logger.info(
            f"KivyMD: Install requirements for '{path_to_project}' project..."
        )
        install_requirements()
        os.remove(os.path.join(path_to_project, "__init__.py"))
        if name_database == "no":
            os.remove(
                os.path.join(path_to_project, "Model", "database_firebase.py")
            )
            os.remove(
                os.path.join(path_to_project, "Model", "database_restdb.py")
            )
    else:
        parser.error(f"The {path_to_project} project already exists")


def create_main_with_hotreload() -> None:
    with open(
        os.path.join(path_to_project, "main.py"), encoding="utf-8"
    ) as main_file:
        main_code = ""
        for string in main_file.readlines():
            main_code += f"# {string}"
    with open(
        os.path.join(path_to_project, "main.py"), "w", encoding="utf-8"
    ) as main_file:
        main_file.write(f"{temp_hot_reload_main}\n{main_code}")

    with open(
        os.path.join(path_to_project, "main.py"), encoding="utf-8"
    ) as main_file:
        main_code = main_file.read()
        main_code = main_code.format(
            "\nfrom kivy.properties import StringProperty\n"
            if use_localization == "yes"
            else "",
            "\nfrom Model.database import DataBase"
            if name_database != "no"
            else "",
            "\nfrom libs.translation import Translation\n"
            if use_localization == "yes"
            else "",
            project_name,
            '\n    lang = StringProperty("en")\n'
            if use_localization == "yes"
            else "",
            "\n        self.base = DataBase()\n"
            if name_database != "no"
            else "",
            "\n        self.translation = Translation(\n"
            '            self.lang, "%s", os.path.join(self.directory, "data", "locales")'
            "\n        )" % project_name
            if use_localization == "yes"
            else "",
            "self.database" if name_database != "no" else "",
            "\n\n    def on_lang(self, instance_app, lang_value: str) -> None:\n"
            "        self.translation.switch_lang(lang_value)\n"
            if use_localization == "yes"
            else "",
            "\n    def switch_lang(self) -> None:\n"
            '        """Switch lang."""\n\n'
            '        self.lang = "ru" if self.lang == "en" else "en"'
            if use_localization == "yes"
            else "",
            project_name,
        )
        with open(
            os.path.join(path_to_project, "main.py"), "w", encoding="utf-8"
        ) as main_module:
            main_module.write(main_code)


def create_main() -> None:
    main_code = temp_main.format(
        "\nfrom kivy.properties import StringProperty\n"
        if use_localization == "yes"
        else "",
        "\nfrom libs.translation import Translation"
        if use_localization == "yes"
        else "",
        "from Model.database import DataBase\n"
        if name_database != "no"
        else "",
        project_name,
        '\n    lang = StringProperty("en")\n'
        if use_localization == "yes"
        else "",
        "\n        self.translation = Translation(\n"
        '            self.lang, "%s", os.path.join(self.directory, "data", "locales")'
        "\n        )" % project_name
        if use_localization == "yes"
        else "",
        "self.database = DataBase()\n" if name_database != "no" else "",
        "self.database" if name_database != "no" else "",
        "\n    def on_lang(self, instance_app, lang_value: str) -> None:\n"
        "        self.translation.switch_lang(lang_value)\n"
        if use_localization == "yes"
        else "",
        "\n    def switch_lang(self) -> None:\n"
        '        """Switch lang."""\n\n'
        '        self.lang = "ru" if self.lang == "en" else "en"\n'
        if use_localization == "yes"
        else "",
        project_name,
    )
    with open(
        os.path.join(path_to_project, "main.py"), "w", encoding="utf-8"
    ) as main_module:
        main_module.write(main_code)


def create_model(
    name_screen: str, module_name: str, name_database: str, path_to_project: str
) -> None:
    if name_database != "no":
        code_model = temp_database_model.format(
            name_screen=name_screen,
            module_name=module_name,
            notify_name_screen=f'"{" ".join(module_name.split("_"))}"',
        )
    else:
        code_model = temp_without_database_model.format(
            module_name=module_name, name_screen=name_screen
        )

    model_module = os.path.join(path_to_project, "Model", module_name)
    with open(f"{model_module}.py", "w", encoding="utf-8") as module:
        module.write(code_model)


def create_basemodel() -> None:
    with open(
        os.path.join(path_to_project, "Model", "base_model.py"),
        "w",
        encoding="utf-8",
    ) as module_basemodel:
        module_basemodel.write(temp_basemodel)


def create_module_basescreen() -> None:
    with open(
        os.path.join(path_to_project, "View", "base_screen.py"),
        "w",
        encoding="utf-8",
    ) as base_screen:
        base_screen.write(temp_base_screen)


def create_controller(
    name_screen: str, module_name: str, use_hotreload: str, path_to_project: str
) -> None:
    name_view = (
        f"View.{name_screen}.{module_name}.{name_screen}View"
        if use_hotreload == "yes"
        else f"{name_screen}View"
    )
    code_controller = temp_code_controller.format(
        name_screen=name_screen,
        module_name=module_name,
        import_module=""
        f"import importlib\n\n"
        f"import View.{name_screen}.{module_name}\n\n"
        f"# We have to manually reload the view module in order to apply the\n"
        f"# changes made to the code on a subsequent hot reload.\n"
        f"# If you no longer need a hot reload, you can delete this instruction.\n"
        f"importlib.reload(View.{name_screen}.{module_name})\n\n"
        if use_hotreload == "yes"
        else f"\nfrom View.{name_screen}.{module_name} import {name_screen}View",
        name_view=name_view,
        get_view=f"View.{name_screen}.{module_name}"
        if use_hotreload == "yes"
        else f"{name_screen}View",
    )

    path_to_controller = os.path.join(path_to_project, "Controller")
    if not os.path.exists(path_to_controller):
        os.mkdir(path_to_controller)
    controller_module = os.path.join(path_to_project, "Controller", module_name)
    with open(f"{controller_module}.py", "w", encoding="utf-8") as module:
        module.write(code_controller)


def create_makefile() -> None:
    makefile = temp_makefile.format(temp_makefile_files[:-2])
    with open(
        os.path.join(path_to_project, "Makefile"), "w", encoding="utf-8"
    ) as make_file:
        make_file.write(makefile)


def create_makefile_data(name_screen: str, module_name: str) -> None:
    global temp_makefile_files

    temp_makefile_files += (
        f"                View/{name_screen}/{module_name}.py \\\n"
    )
    temp_makefile_files += (
        f"                View/{name_screen}/{module_name}.kv \\\n"
    )


def create_screens_data(name_screen: str, module_name: str) -> None:
    global temp_screens_imports
    global temp_screens_data

    temp_screens_imports += (
        f"from Model.{module_name} import {name_screen}Model\n"
        f"from Controller.{module_name} import {name_screen}Controller\n"
    )
    temp_screens_data += (
        '\n    %s: {\n        "model": %s,'
        '\n        "controller": %s,\n    },\n'
        % (
            f'"{" ".join(module_name.split("_"))}"',
            f"{name_screen}Model",
            f"{name_screen}Controller",
        )
    )


def create_module_screens() -> None:
    path_to_module_screens = os.path.join(path_to_project, "View", "screens.py")
    with open(path_to_module_screens, "w", encoding="utf-8") as module_screens:
        module_screens.write(
            "%s\nscreens = {%s}" % (temp_screens_imports, temp_screens_data)
        )


def create_common_responsive_module(
    use_responsive: list, path_to_project: str
) -> None:
    for name_screen in use_responsive:
        path_to_init_common = os.path.join(
            path_to_project, "View", name_screen, "components", "common"
        )
        os.makedirs(path_to_init_common)
        with open(
            os.path.join(path_to_init_common, "__init__.py"),
            "w",
            encoding="utf-8",
        ) as init_common_components:
            init_common_components.write(
                "# This directory is for common responsive design components\n"
            )


def create_view(
    name_screen: str,
    module_name: str,
    use_responsive: list,
    path_to_project: str,
) -> None:
    path_to_view = os.path.join(path_to_project, "View", name_screen)
    path_to_components = os.path.join(path_to_view, "components")
    view_module = os.path.join(path_to_view, module_name)
    os.makedirs(path_to_view)
    os.makedirs(path_to_components)

    with open(
        os.path.join(path_to_view, "__init__.py"), "w", encoding="utf-8"
    ) as init_module:
        init_module.write("")
    with open(f"{view_module}.py", "w", encoding="utf-8") as view_file:
        view_file.write(
            temp_code_view.format(name_screen=name_screen)
            if name_screen not in use_responsive
            else temp_code_responsive_view.format(name_screen=name_screen)
        )

    if name_screen in use_responsive:
        for name_platform in ["DesktopScreen", "MobileScreen", "TabletScreen"]:
            path_to_init_components = os.path.join(
                path_to_project,
                "View",
                name_screen,
                "components",
                "__init__.py",
            )
            path_to_platforms = os.path.join(
                path_to_project, "View", name_screen, "components", "platforms"
            )
            path_to_platform = os.path.join(path_to_platforms, name_platform)
            path_to_platform_components = os.path.join(
                path_to_platform, "components"
            )
            os.makedirs(path_to_platform_components)
            shutil.copy(
                os.path.join(path_to_view, "__init__.py"),
                path_to_platform_components,
            )
            shutil.copy(
                os.path.join(path_to_view, "__init__.py"), path_to_platforms
            )

            name_platform_module = (
                f'{name_platform.split("Screen")[0].lower()}_screen'
            )
            with open(
                os.path.join(path_to_platform, f"{name_platform_module}.kv"),
                "w",
                encoding="utf-8",
            ) as platform_rule:
                platform_rule.write(f"<{name_platform}View>\n")
            with open(
                os.path.join(path_to_platform, f"{name_platform_module}.py"),
                "w",
                encoding="utf-8",
            ) as platform_baseclass:
                platform_baseclass.write(
                    temp_responsive_platform_baseclass.format(name_platform)
                )

        with open(
            path_to_init_components, "w", encoding="utf-8"
        ) as init_components:
            init_components.write(temp_responsive_component_imports)

    with open(f"{view_module}.kv", "w", encoding="utf-8") as view_file:
        view_file.write(f"<{name_screen}View>\n")

    if name_screen not in use_responsive:
        shutil.copy(
            os.path.join(path_to_view, "__init__.py"), path_to_components
        )


def create_package_utility() -> None:
    path_to_utility = os.path.join(path_to_project, "Utility")
    os.mkdir(path_to_utility)

    with open(
        os.path.join(path_to_utility, "__init__.py"), "w", encoding="utf-8"
    ) as init_module:
        init_module.write("")
    with open(
        os.path.join(path_to_utility, "observer.py"), "w", encoding="utf-8"
    ) as observer:
        observer.write(temp_utility)


def create_requirements() -> None:
    with open(
        os.path.join(path_to_project, "requirements.txt"), "w", encoding="utf-8"
    ) as requirements:
        requirements.write(
            firebase_requirements
            if name_database == "firebase"
            else without_firebase_requirements
        )


def create_virtual_environment() -> None:
    os.system(f"{python_version} -m pip install virtualenv")
    os.system(
        f"virtualenv -p {python_version} {os.path.join(path_to_project, 'venv')}"
    )


def install_requirements() -> None:
    python = os.path.join(path_to_project, "venv", "bin", "python3")
    if kivy_version == "master":
        if platform == "macosx":
            os.system(
                f"{python} -m pip install 'kivy[base] @ https://github.com/kivy/kivy/archive/master.zip'"
            )
        else:
            os.system(
                f"{python} -m pip install https://github.com/kivy/kivy/archive/master.zip"
            )
    elif kivy_version == "stable":
        os.system(f"{python} -m pip install kivy")
    else:
        os.system(f"{python} -m pip install kivy=={kivy_version}")
    os.system(
        f"{python} -m pip install https://github.com/kivymd/KivyMD/archive/master.zip"
    )
    os.system(f"{python} -m pip install watchdog")
    if name_database == "firebase":
        os.system(
            f"{python} -m pip install "
            f"multitasking "
            f"firebase "
            f"firebase-admin "
            f"python_jwt "
            f"gcloud "
            f"sseclient "
            f"pycryptodome==3.4.3 "
            f"requests_toolbelt "
            f"watchdog "
        )
    os.system(
        f"{os.path.join(path_to_project, 'venv', 'bin', 'python3')} -m pip list"
    )


def check_databases() -> None:
    databases = {"firebase": "restdb", "restdb": "firebase"}
    os.remove(
        os.path.join(
            path_to_project, "Model", f"database_{databases[name_database]}.py"
        )
    )
    os.rename(
        os.path.join(path_to_project, "Model", f"database_{name_database}.py"),
        os.path.join(path_to_project, "Model", "database.py"),
    )


def chek_camel_case_name_project(name_project) -> Union[bool, list]:
    result = re.findall("[A-Z][a-z]*", name_project)
    if len(result) == 1:
        return False
    return result


def create_argument_parser() -> ArgumentParserWithHelp:
    parser = ArgumentParserWithHelp(
        prog="create_project.py",
        allow_abbrev=False,
    )
    parser.add_argument(
        "pattern",
        help="the name of the pattern with which the project will be created.",
    )
    parser.add_argument(
        "directory",
        help="directory in which the project will be created.",
    )
    parser.add_argument(
        "name",
        help="project name.",
    )
    parser.add_argument(
        "python_version",
        help="the version of Python (specify as `python3.9` or `python3.8`) "
        "with which the virtual environment will be created.",
    )
    parser.add_argument(
        "kivy_version",
        help="version of Kivy (specify as `2.1.0` or `master`) that will be "
        "used in the project.",
    )
    parser.add_argument(
        "--name_screen",
        nargs="*",
        type=str,
        default=["MainScreen"],
        help="the name/names of the class which be used when creating the project pattern.",
    )
    parser.add_argument(
        "--use_responsive",
        nargs="*",
        type=str,
        default=[],
        help="the name/names of the views to be used by the responsive UI.",
    )
    parser.add_argument(
        "--name_database",
        default="no",
        help="name of the database provider ('firebase' or 'restdb').",
    )
    parser.add_argument(
        "--use_hotreload",
        default="no",
        help="creates a hot reload entry point to the application.",
    )
    parser.add_argument(
        "--use_localization",
        default="no",
        help="creates application localization files.",
    )
    return parser


if __name__ == "__main__":
    main()
