<?php

class ModuleModuleManager extends Module
{
  public UpdateManager $UpdateManager;

  function __construct(System $System)
  {
    parent::__construct($System);
    $this->Name = 'ModuleManager';
    $this->Version = '1.0';
    $this->Creator = 'Chronos';
    $this->License = 'GNU/GPLv3';
    $this->Description = 'User interface for module management.';
    $this->Dependencies = array(ModuleSetup::GetName());
    $this->Revision = 1;
    $this->Type = ModuleType::System;
    $this->Models = array(ModelField::GetClassName(), ModuleDependency::GetClassName());
  }

  function DoStart(): void
  {
    $this->Manager->OnInstallModel = array($this, 'InstallModel');
    $this->Manager->OnUninstallModel = array($this, 'UninstallModel');
    $this->Manager->OnInstallModule = array($this, 'InstallModule');
    $this->Manager->OnUninstallModule = array($this, 'UninstallModule');

    Core::Cast($this->System)->RegisterPage(['modules'], 'PageModules');
    //$this->Manager->OnModuleChange = array($this, 'ModuleChange');
    //$this->LoadFromDatabase();

    $this->System->FormManager->RegisterClass('Module', array(
      'Title' => 'Moduly',
      'Table' => 'Module',
      'Items' => array(
        'Name' => array('Type' => 'String', 'Caption' => 'Systémové jméno', 'Default' => ''),
        'Title' => array('Type' => 'String', 'Caption' => 'Název', 'Default' => ''),
        'Description' => array('Type' => 'Text', 'Caption' => 'Popis', 'Default' => ''),
        'Version' => array('Type' => 'String', 'Caption' => 'Verze', 'Default' => ''),
        'License' => array('Type' => 'String', 'Caption' => 'Licence', 'Default' => ''),
        'Creator' => array('Type' => 'String', 'Caption' => 'Tvůrce', 'Default' => ''),
        'HomePage' => array('Type' => 'Hyperlink', 'Caption' => 'Domovské stránky', 'Default' => ''),
        'Installed' => array('Type' => 'Boolean', 'Caption' => 'Instalováno', 'Default' => '', 'ReadOnly' => true),
        'Models' => array('Type' => 'TModelListModule', 'Caption' => 'Modely', 'Default' => ''),
        'Links' => array('Type' => 'TModuleLinkListModule', 'Caption' => 'Vazby', 'Default' => ''),
      ),
      'Actions' => array(
        array('Caption' => 'Aktualizovat z disku', 'URL' => '/module/?A=SaveToDb'),
      ),
    ));
    $this->System->FormManager->RegisterFormType('TModule', array(
      'Type' => 'Reference',
      'Table' => 'Module',
      'Id' => 'Id',
      'Name' => 'Title',
      'Filter' => '1',
    ));
    $this->System->FormManager->RegisterFormType('TModelListModule', array(
      'Type' => 'ManyToOne',
      'Table' => 'Model',
      'Id' => 'Id',
      'Ref' => 'Module',
      'Filter' => '1',
    ));
    $this->System->FormManager->RegisterClass('Model', array(
      'Title' => 'Modely',
      'Table' => 'Model',
      'Items' => array(
        'Name' => array('Type' => 'String', 'Caption' => 'Systémové jméno', 'Default' => ''),
        'Title' => array('Type' => 'String', 'Caption' => 'Název', 'Default' => ''),
        'Module' => array('Type' => 'TModule', 'Caption' => 'Module', 'Default' => ''),
        'Query' => array('Type' => 'String', 'Caption' => 'SQL dotaz', 'Default' => ''),
        'DefaultSortColumn' => array('Type' => 'String', 'Caption' => 'Výchozí sloupce řazení', 'Default' => ''),
        'DefaultSortOrder' => array('Type' => 'Text', 'Caption' => 'Výchozí směr řazení', 'Default' => ''),
        'Fields' => array('Type' => 'TModelFieldListModel', 'Caption' => 'Pole', 'Default' => ''),
      ),
    ));
    $this->System->FormManager->RegisterFormType('TModel', array(
      'Type' => 'Reference',
      'Table' => 'Model',
      'Id' => 'Id',
      'Name' => 'Title',
      'Filter' => '1',
    ));
    $this->System->FormManager->RegisterFormType('TModelFieldListModel', array(
      'Type' => 'ManyToOne',
      'Table' => 'ModelField',
      'Id' => 'Id',
      'Ref' => 'Model',
      'Filter' => '1',
    ));
    $this->System->FormManager->RegisterClass('ModelField', array(
      'Title' => 'Pole modelu',
      'Table' => 'ModelField',
      'Items' => array(
        'Name' => array('Type' => 'String', 'Caption' => 'Systémové jméno', 'Default' => ''),
        'Title' => array('Type' => 'String', 'Caption' => 'Název', 'Default' => ''),
        'Model' => array('Type' => 'TModel', 'Caption' => 'Model', 'Default' => ''),
        'Query' => array('Type' => 'String', 'Caption' => 'SQL dotaz', 'Default' => ''),
        'Type' => array('Type' => 'String', 'Caption' => 'Typ', 'Default' => ''),
        'DefaultValue' => array('Type' => 'String', 'Caption' => 'Výchozí hodnota', 'Default' => ''),
        'IsNull' => array('Type' => 'Boolean', 'Caption' => 'Také nulová hodnota', 'Default' => ''),
        'Suffix' => array('Type' => 'String', 'Caption' => 'Text za', 'Default' => ''),
      ),
    ));
    $this->System->FormManager->RegisterFormType('TModuleLink', array(
      'Type' => 'Reference',
      'Table' => 'ModuleLink',
      'Id' => 'Id',
      'Name' => 'Module',
      'Filter' => '1',
    ));
    $this->System->FormManager->RegisterFormType('TModuleLinkListModule', array(
      'Type' => 'ManyToOne',
      'Table' => 'ModuleLink',
      'Id' => 'Id',
      'Ref' => 'Module',
      'Filter' => '1',
    ));
    $this->System->FormManager->RegisterClass('ModuleLink', array(
      'Title' => 'Vazby modulu',
      'Table' => 'ModuleLink',
      'Items' => array(
        'Module' => array('Type' => 'TModule', 'Caption' => 'Modul', 'Default' => ''),
        'LinkedModule' => array('Type' => 'TModule', 'Caption' => 'Vázaný modul', 'Default' => ''),
        'Type' => array('Type' => 'String', 'Caption' => 'Typ vazby', 'Default' => ''),
      ),
    ));
    $this->System->FormManager->RegisterFormType('TModule', array(
      'Type' => 'Reference',
      'Table' => 'Module',
      'Id' => 'Id',
      'Name' => 'Name',
      'Filter' => '1',
    ));
  }

  function DoStop(): void
  {
    Core::Cast($this->System)->UnregisterPage(['modules']);
  }

  function DoBeforeInstall(): void
  {
    $this->AddModelDatabase(Module::GetModelDesc());
    $this->Manager->OnInstallModule = array($this, 'InstallModule');
    $this->Manager->OnUninstallModule = array($this, 'UninstallModule');
    $this->AddModelDatabase(Model::GetModelDesc());
    $this->Manager->OnInstallModel = array($this, 'InstallModel');
    $this->Manager->OnUninstallModel = array($this, 'UninstallModel');
  }

  function DoAfterUninstall(): void
  {
    $this->Manager->OnInstallModel = null;
    $this->Manager->OnUninstallModel = null;
    $this->RemoveModelDatabase(Model::GetModelDesc());
    $this->Manager->OnInstallModule = null;
    $this->Manager->OnUninstallModule = null;
    $this->RemoveModelDatabase(Module::GetModelDesc());
  }

  function DoInstall(): void
  {
    $this->InstallModel(Module::GetModelDesc(), $this->System->GetModule($this::GetName()));
    $this->InstallModel(Model::GetModelDesc(), $this->System->GetModule($this::GetName()));
  }

  function AddModelDatabase(ModelDesc $ModelDesc)
  {
    $Query = "CREATE TABLE IF NOT EXISTS `".$ModelDesc->Name."` (\n";
    $Query .= '  `'.$ModelDesc->PrimaryKey.'` int(11) NOT NULL AUTO_INCREMENT,'."\n";
    foreach ($ModelDesc->Columns as $Column)
    {
      $Query .= "  `".$Column->Name."` ";
      if ($Column->Type == ModelColumnType::Integer) $Query .= 'int(11)';
      else if ($Column->Type == ModelColumnType::String) $Query .= 'varchar(255)';
      else if ($Column->Type == ModelColumnType::Float) $Query .= 'varchar(255)';
      else if ($Column->Type == ModelColumnType::Text) $Query .= 'text';
      else if ($Column->Type == ModelColumnType::DateTime) $Query .= 'datetime';
      else if ($Column->Type == ModelColumnType::Reference) $Query .= 'int(11)';
      else if ($Column->Type == ModelColumnType::Boolean) $Query .= 'tinyint(1)';
      else if ($Column->Type == ModelColumnType::Date) $Query .= 'date';
      else if ($Column->Type == ModelColumnType::BigInt) $Query .= 'bigint(20)';
      else if ($Column->Type == ModelColumnType::Enum)
      {
        $Query .= 'enum("'.implode('", "', $Column->States).'")';
      }

      if ($Column->Nullable) $Query .= '';
        else $Query .= ' NOT NULL';

      $Query .= ' COLLATE utf8_general_ci';

      if ($Column->HasDefault)
      {
        if ($Column->Default == null)
          $Query .= ' DEFAULT NULL';
        else $Query .= ' DEFAULT '.$Column->GetDefault();
      }
      $Query .= ",\n";
    }
    $Query .= '  PRIMARY KEY (`'.$ModelDesc->PrimaryKey.'`)';
    foreach ($ModelDesc->Columns as $Column)
    {
      if ($Column->Type == ModelColumnType::Reference)
        $Query .= ','."\n".'  KEY `'.$Column->Name.'` (`'.$Column->Name.'`)';
      else if ($Column->Unique)
        $Query .= ','."\n".'  UNIQUE KEY `'.$Column->Name.'` (`'.$Column->Name.'`)';
    }
    $Query .= "\n";

    if ($ModelDesc->Memory) $Engine = 'MEMORY';
      else $Engine = 'InnoDB';
    $Query .= ') ENGINE='.$Engine.' DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;';
    foreach ($ModelDesc->Columns as $Column)
    {
      if ($Column->Type == ModelColumnType::Reference)
        $Query .= "ALTER TABLE `".$ModelDesc->Name."` ".
        "ADD CONSTRAINT `".$ModelDesc->Name."_ibfk_".$Column->Name."` FOREIGN KEY (`".$Column->Name."`) REFERENCES `".$Column->RefTable."` (`Id`);";
    }
    $this->Database->query($Query);
  }

  function RemoveModelDatabase(ModelDesc $ModelDesc)
  {
    /*$Query = '';
    foreach ($ModelDesc->Columns as $Column)
    {
      if ($Column->Type == ModelColumnType::Reference)
        $Query .= "ALTER TABLE `".$ModelDesc->Name."` ".
        "DROP FOREIGN KEY `".$ModelDesc->Name."_ibfk_".$Column->Name."`;";
    }
    if (($Query != '') and $this->Database->TableExists($ModelDesc->Name))
    {
      $this->Database->query($Query);
    }
*/
    $this->Database->query('SET foreign_key_checks = 0; DROP TABLE IF EXISTS `'.$ModelDesc->Name.'`');
  }

  function InstallModel(ModelDesc $ModelDesc, Module $Module)
  {
    $this->AddModelDatabase($ModelDesc);
    $this->Database->insert('Model', array('Name' => $ModelDesc->Name, 'Module' => $Module->Id));
    $ModelId = $this->Database->insert_id;
    foreach ($ModelDesc->Columns as $Field)
    {
      $this->Database->insert('ModelField', array('Name' => $Field->Name, 'Model' => $ModelId,
        'Type' => ModelColumnType::GetName($Field->Type), 'Nullable' => (int)$Field->Nullable));
    }
    if ($ModelDesc->DefaultValues != null)
    {
      $Values = call_user_func('self::'.$ModelDesc->DefaultValues);
      foreach ($Values as $Value)
      {
        $this->Database->insert($ModelDesc->Name, $Value);
      }
    }
  }

  function UninstallModel(ModelDesc $ModelDesc, Module $Module)
  {
    $DbResult = $this->Database->select('Model', 'Id', '(Name="'.$ModelDesc->Name.'") AND (Module='.$Module->Id.')');
    if ($DbResult->num_rows > 0)
    {
      $DbRow = $DbResult->fetch_assoc();
      $ModelId = $DbRow['Id'];
      $DbResult = $this->Database->delete('ModelField', '(Model='.$ModelId.')');
      $this->Database->delete('Model', '(Id='.$ModelId.')');
    } else echo('Can\'t remove model '.$ModelDesc->Name.'<br/>');
    $this->RemoveModelDatabase($ModelDesc);
  }

  function InstallModule(Module $Module)
  {
    $this->Database->insert('Module', array(
      'Id' => $Module->Id,
      'Name' => $Module->Name,
      'Title' => $Module->Title,
      'Creator' => $Module->Creator,
      'Version' => $Module->Version,
      'License' => $Module->License,
      'Installed' => (int)$Module->Installed,
      'Description' => $Module->Description,
      'InstalledVersion' => $Module->InstalledVersion
    ));
  }

  function UninstallModule(Module $Module)
  {
    $this->Database->delete('Module', 'Name="'.$Module->Name.'"');
  }

  function ModuleChange(Module $Module): void
  {
    if ($Module->Installed) $Installed = 1;
      else $Installed = 0;
    $this->Database->query('UPDATE `Module` SET `Installed`='.$Installed.' WHERE `Name`="'.$Module->Name.'"');
  }

  function LoadFromDatabase(): void
  {
    //DebugLog('Loading modules...');
    $this->Modules = array();
    $Query = 'SELECT `Id`, `Name`,`Installed` FROM `Module`';
    $DbResult = $this->Database->query($Query);
    while ($Module = $DbResult->fetch_array())
    {
      include_once('Modules/'.$Module['Name'].'/'.$Module['Name'].'.php');
      $ModuleClassName = 'Module'.$Module['Name'];
      $NewModule = new $ModuleClassName($this->Database, $this->Manager);
      $NewModule->Id = $Module['Id'];
      $NewModule->Installed = $Module['Installed'];
      $this->Manager->RegisterModule($NewModule);
    }
  }

  function SaveToDatabase(): string
  {
    $Output = '';
    $Modules = array();
    $DbResult = $this->Database->query('SELECT * FROM `Module`');
    while ($DbRow = $DbResult->fetch_assoc())
    {
      $Modules[$DbRow['Name']] = $DbRow;
      if ($this->System->ModuleManager->ModulePresent($DbRow['Name']))
        $this->System->ModuleManager->GetModule($DbRow['Name'])->Id = $DbRow['Id'];
    }

    // Add missing
    foreach ($this->System->ModuleManager->Modules as $Module)
    {
      if (!array_key_exists($Module->Name, $Modules))
      {
        $this->Database->insert('Module', array('Name' => $Module->Name,
          'Version' => $Module->Version, 'Creator' => $Module->Creator,
          'HomePage' => $Module->HomePage, 'Title' => $Module->Title,
          'Description' => $Module->Description, 'License' => $Module->License,
          'Installed' => $Module->Installed));
        $this->System->ModuleManager->GetModule($Module->Name)->Id = $this->Database->insert_id;
      }
      else $this->Database->update('Module', 'Name = "'.$Module->Name.'"', array(
        'Version' => $Module->Version, 'Creator' => $Module->Creator,
        'HomePage' => $Module->HomePage, 'Title' => $Module->Title,
        'Description' => $Module->Description, 'License' => $Module->License,
        'Installed' => $Module->Installed));
    }

    // Remove exceeding
    foreach ($Modules as $Module)
    if (!$this->System->ModuleManager->ModulePresent($Module['Name']))
    {
      $Output .= 'Removing module '.$Module['Name'].' from list</br/>';
      $this->Database->query('DELETE FROM `ModuleLink` WHERE `Module` = '.$Module['Id']);
      $this->Database->query('DELETE FROM `ModuleLink` WHERE `LinkedModule` = '.$Module['Id']);
      $DbResult = $this->Database->query('SELECT Id FROM `PermissionOperation` WHERE `Module` = '.$Module['Id']);
      while ($DbRow = $DbResult->fetch_assoc())
      {
        $this->Database->query('DELETE FROM `PermissionGroupAssignment` WHERE `AssignedOperation` = '.$DbRow['Id']);
        $this->Database->query('DELETE FROM `PermissionUserAssignment` WHERE `AssignedOperation` = '.$DbRow['Id']);
      }
      $this->Database->query('DELETE FROM `PermissionOperation` WHERE `Module` = '.$Module['Id']);
      $this->Database->query('DELETE FROM `Model` WHERE `Module` = '.$Module['Id']);
      $DbResult = $this->Database->query('SELECT Id FROM `Model` WHERE `Module` = '.$Module['Id']);
      while ($DbRow = $DbResult->fetch_assoc())
        $this->Database->query('DELETE FROM `ModelField` WHERE `Model` = '.$DbRow['Id']);
      $this->Database->query('DELETE FROM `Module` WHERE `Id` = '.$Module['Id']);
    }

    // Reload dependencies
    $DbDependency = array();
    $DbResult = $this->Database->query('SELECT * FROM `ModuleLink`');
    while ($DbRow = $DbResult->fetch_assoc())
      $DbDependency[$DbRow['Module']][] = $DbRow['LinkedModule'];

    foreach ($this->System->ModuleManager->Modules as $Module)
    {
      // Add missing
      foreach ($Module->Dependencies as $Dependency)
      {
        if (!array_key_exists($Module->Id, $DbDependency) or
        !in_array($this->System->ModuleManager->GetModule($Dependency)->Id, $DbDependency[$Module->Id]))
        {
          if ($this->System->ModuleManager->ModulePresent($Dependency))
            $DependencyId = $this->System->ModuleManager->GetModule($Dependency)->Id;
            else throw new Exception('Dependent module '.$Dependency.' not found');
          $this->Database->insert('ModuleLink', array('Module' => $Module->Id,
            'LinkedModule' => $DependencyId, 'Type' => 'DependOn'));
        }
      }

      // Remove exceeding
      if (array_key_exists($Module->Id, $DbDependency))
      foreach ($DbDependency[$Module->Id] as $Dep)
      {
        $DepModName = $this->System->ModuleManager->SearchModuleById($Dep);
        if (!in_array($DepModName, $Module->Dependencies))
        $this->Database->query('DELETE FROM `ModuleLink` WHERE `Module` = '.
          $Module->Id.' AND LinkedModule='.$Dep);
      }
    }
    return $Output;
  }
}

class PageModules extends Page
{
  public array $YesNo;

  function __construct(System $System)
  {
    parent::__construct($System);
    $this->Title = T('Modules');
    $this->ParentClass = 'PageSetup';
    $this->YesNo = array(false => T('No'), true => T('Yes'));
  }

  function Show(): string
  {
    $Output = '';
    if (array_key_exists('op', $_GET)) $Operation = $_GET['op'];
      else $Operation = '';
    if ($Operation == 'install')
    {
      $this->System->ModuleManager->GetModule($_GET['name'])->Install();
      $this->System->ModuleManager->SaveState();
      $Output .= 'Modul '.$_GET['name'].' instalován.<br/>';
    } else
    if ($Operation == 'uninstall')
    {
      $this->System->ModuleManager->GetModule($_GET['name'])->Uninstall();
      $this->System->ModuleManager->SaveState();
      $Output .= 'Modul '.$_GET['name'].' odinstalován.<br/>';
    } else
    if ($Operation == 'enable')
    {
      $this->System->ModuleManager->GetModule($_GET['name'])->Enable();
      $this->System->ModuleManager->SaveState();
      $Output .= 'Modul '.$_GET['name'].' povolen.<br/>';
    } else
    if ($Operation == 'disable')
    {
      $this->System->ModuleManager->GetModule($_GET['name'])->Disable();
      $this->System->ModuleManager->SaveState();
      $Output .= 'Modul '.$_GET['name'].' zakázán.<br/>';
    } else
    if ($Operation == 'upgrade')
    {
      $this->System->ModuleManager->GetModule($_GET['name'])->Upgrade();
      $this->System->ModuleManager->SaveState();
      $Output .= 'Modul '.$_GET['name'].' povýšen.<br/>';
    } else
    if ($Operation == 'install-all')
    {
      $this->System->ModuleManager->InstallAll();
      $this->System->ModuleManager->SaveState();
      $Output .= 'Všechny moduly instalovány.<br/>';
    } else
    if ($Operation == 'uninstall-all')
    {
      $this->System->ModuleManager->UninstallAll(array(ModuleCondition::Application));
      $this->System->ModuleManager->UninstallAll(array(ModuleCondition::Library));
      $this->System->ModuleManager->SaveState();
      $Output .= 'Všechny moduly odinstalovány.<br/>';
    } else
    if ($Operation == 'enable-all')
    {
      $this->System->ModuleManager->EnableAll();
      $this->System->ModuleManager->SaveState();
      $Output .= 'Všechny moduly povoleny.<br/>';
    } else
    if ($Operation == 'disable-all')
    {
      $this->System->ModuleManager->DisableAll();
      $this->System->ModuleManager->SaveState();
      $Output .= 'Všechny moduly zakázány.<br/>';
    } else
    if ($Operation == 'upgrade-all')
    {
      $this->System->ModuleManager->UpgradeAll();
      $this->System->ModuleManager->SaveState();
      $Output .= 'Všechny moduly povýšeny.<br/>';
    }
    $Output .= '<h3>Správa modulů</h3>';
    $Output .= $this->ShowList();
    return $Output;
  }

  function ShowList(): string
  {
    $Output = '';

    $Pageing = new Paging();
    $Pageing->TotalCount = count($this->System->ModuleManager->Modules);
    $Table = new VisualTable();
    $Table->SetColumns(array(
      array('Name' => 'Name', 'Title' => T('Name')),
      array('Name' => 'Creator', 'Title' => T('Creator')),
      array('Name' => 'Version', 'Title' => T('Version')),
      array('Name' => 'InstalledVersion', 'Title' => T('Installed version')),
      array('Name' => 'Type', 'Title' => T('Type')),
      array('Name' => 'License', 'Title' => T('License')),
      array('Name' => 'Installed', 'Title' => T('Installed')),
      array('Name' => 'Enabled', 'Title' => T('Enabled')),
      array('Name' => 'Description', 'Title' => T('Description')),
      array('Name' => 'Dependencies', 'Title' => T('Dependencies')),
      array('Name' => '', 'Title' => 'Akce'),
    ));
    $ModuleType = array(T('System'), T('Library'), T('Application'));
    foreach ($this->System->ModuleManager->Modules as $Module)
    {
      if (($Module->Dependencies) > 0) $Dependencies = implode(',', $Module->Dependencies);
       else $Dependencies = '&nbsp;';
      $Actions = '';
      if ($Module->Type != ModuleType::System)
      {
        if ($Module->Installed == true)
        {
          $Actions .= ' <a href="?op=uninstall&amp;name='.$Module->Name.'">Odinstalovat</a>';
          if ($Module->Enabled == true) $Actions .= ' <a href="?op=disable&amp;name='.$Module->Name.'">Zakázat</a>';
          else $Actions .= ' <a href="?op=enable&amp;name='.$Module->Name.'">Povolit</a>';
          if ($Module->InstalledVersion != $Module->Version) $Actions .= ' <a href="?op=upgrade&amp;name='.$Module->Name.'">Povýšit</a>';
        } else $Actions .= ' <a href="?op=install&amp;name='.$Module->Name.'">Instalovat</a>';
      }

      $Table->Table->Cells[] = array($Module->Name,
        $Module->Creator, $Module->Version, $Module->InstalledVersion, $ModuleType[$Module->Type],
        $Module->License, $this->YesNo[$Module->Installed],
        $this->YesNo[$Module->Enabled], $Module->Description,
        $Dependencies, $Actions);
    }
    $Output .= $Pageing->Show();
    $Output .= $Table->Show();
    $Output .= $Pageing->Show();

    $Output .= '<div>'.
      '<a href="?op=install-all">Instalovat vše</a> '.
      '<a href="?op=uninstall-all">Odinstalovat vše</a> '.
      '<a href="?op=disable-all">Zakázat vše</a> '.
      '<a href="?op=enable">Povolit vše</a> '.
      '<a href="?op=upgrade">Povýšit vše</a> '.
      '</div>';

    return $Output;
  }
}

class ModelField extends Model
{
  static function GetModelDesc(): ModelDesc
  {
    $Desc = new ModelDesc(self::GetClassName());
    $Desc->AddString('Name');
    $Desc->AddReference('Model', Model::GetClassName());
    $Desc->AddString('Type');
    $Desc->AddBoolean('Nullable');
    return $Desc;
  }
}

class ModuleDependency extends Model
{
  static function GetModelDesc(): ModelDesc
  {
    $Desc = new ModelDesc(self::GetClassName());
    $Desc->AddReference('Module', Module::GetClassName());
    $Desc->AddReference('Dependency', Module::GetClassName());
    return $Desc;
  }
}