<?php

class ConfigRouterOSQueue extends NetworkConfigItem
{
  var $UsedNames;
  var $Devices;
  var $QueueItems;

  function CheckName($Name)
  {
    if(in_array($Name, $this->UsedNames)) die("\n".'Duplicate name: '.$Name);
      else $this->UsedNames[] = $Name;
  }

  function Run()
  {
    $PathQueue = array('queue', 'tree');

    $Routerboard = new Routerboard();
    $Routerboard->UserName = $this->System->Config['MainRouter']['UserName'];
    $Routerboard->Timeout = $this->System->Config['MainRouter']['ConnectTimeout'];
    $Routerboard->HostName = $this->System->Config['MainRouter']['HostName'];
    $Routerboard->Debug = true;

    $this->UsedNames = array();

    $Finance = &$this->System->Modules['Finance'];
    $Finance->LoadMonthParameters(0);

    // Generate traffic shaping rules
    //$TotalMaxSpeedIn = 4048; //$RealMaxSpeed; //1536;
    //TotalMaxSpeedOut = 3048; //$RealMaxSpeed; //1536;
    //$UsersMaxSpeedIn = 1900; //$MaxSpeed;
    //$UsersMaxSpeedOut = 1900; //$MaxSpeed;

    $InDivider = 1;
    $OutDivider = 1;
    $TotalMaxSpeedIn = round($Finance->RealMaxSpeed / $InDivider) * 1000;
    $TotalMaxSpeedOut = round($Finance->RealMaxSpeed / $OutDivider) * 1000;
    $UsersMaxSpeedIn = round($Finance->MaxSpeed / $InDivider) * 1000;
    $UsersMaxSpeedOut = round($Finance->MaxSpeed / $OutDivider) * 1000;
    $VoipMaxSpeedIn = $TotalMaxSpeedIn - 136;
    $VoipMaxSpeedOut = $TotalMaxSpeedOut - 136;
    $VoipSpeedIn = 100; //$SpeedReserve;
    $VoipSpeedOut = 100; //$SpeedReserve;
    $OutInterface = 'eth1';
    $InInterface = 'ifb0';
    $InetInterface = $this->System->Config['MainRouter']['InetInterface'];

    $DbResult = $this->Database->select('Service', '*', '(`ChangeAction` IS NULL) AND (`Id`='.TARIFF_FREE.')');
    if($DbResult->num_rows == 1)
    {
      $Service = $DbResult->fetch_array();
      $FreeInetSpeed = $Service['InternetSpeedMax'];
    } else $FreeInetSpeed = 0;

    $ItemsQueue = array();

    // Root of tree and main limit
    $ItemsQueue[] = array('name' => 'main-out', 'limit-at' => $UsersMaxSpeedIn, 'max-limit' => $UsersMaxSpeedIn, 'parent' => 'global');
    $ItemsQueue[] = array('name' => 'main-in', 'limit-at' => $UsersMaxSpeedOut, 'max-limit' => $UsersMaxSpeedOut, 'parent' => 'global');

    // Slow free internet
    $PacketMark = GetMarkByComment('free-out');
    $ItemsQueue[] = array('name' => 'free-out', 'limit-at' => $FreeInetSpeed, 'max-limit' => $FreeInetSpeed, 'parent' => 'main-out', 'packet-mark' => $PacketMark);
    $PacketMark = GetMarkByComment('free-in');
    $ItemsQueue[] = array('name' => 'free-in', 'limit-at' => $FreeInetSpeed, 'max-limit' => $FreeInetSpeed, 'parent' => 'main-in', 'packet-mark' => $PacketMark);

    // Process users
    $DbResult = $this->Database->query('SELECT `Member`.*, `Subject`.`Name` FROM `Member` '.
      'LEFT JOIN `Subject` ON `Subject`.`Id` = `Member`.`Subject` WHERE `Member`.`Blocked`=0');
    while($Member = $DbResult->fetch_assoc())
    {
      $ServiceIndex = 1;
      echo('Zákazník '.$Member['Name']."\n");
      $DbResult4 = $this->Database->query('SELECT `Service`.*, `ServiceCustomerRel`.`Id` AS `RelId` FROM `ServiceCustomerRel` '.
        'JOIN `Service` ON `Service`.`Id` = `ServiceCustomerRel`.`Service` '.
        'WHERE (`ServiceCustomerRel`.`Customer` = '.$Member['Id'].') AND (`ServiceCustomerRel`.`ChangeAction` IS NULL) '.
        'AND (`Service`.`InternetSpeedMax` > 0) AND (`Service`.`InternetSpeedMin` > 0)');
      while($Service = $DbResult4->fetch_assoc())
      {
        echo('Služba '.$Service['Name'].': ');
        $MemberName = RouterOSIdent($Member['Name'].'-'.$Member['Id'].'-'.$ServiceIndex);
        $SpeedIn = round($Service['InternetSpeedMin'] / $InDivider);
        $SpeedOut = round($Service['InternetSpeedMin'] / $OutDivider);
        $UserMaxSpeedIn = round($Service['InternetSpeedMax'] / $InDivider);
        $UserMaxSpeedOut = round($Service['InternetSpeedMax'] / $OutDivider);
        $ItemsQueue[] = array('name' => $MemberName.'-out', 'limit-at' => $SpeedIn, 'max-limit' => $UserMaxSpeedIn, 'parent' => 'main-out');
        $ItemsQueue[] = array('name' => $MemberName.'-in', 'limit-at' => $SpeedOut, 'max-limit' => $UserMaxSpeedOut, 'parent' => 'main-in');
        $this->CheckName($MemberName.'-out');
        $this->CheckName($MemberName.'-in');

        $Filter = '(`Used` = 1) AND (`Service` = '.$Service['RelId'].')';
        $DbResult2 = $this->Database->select('NetworkDevice', 'COUNT(*)', $Filter);
        $Row = $DbResult2->fetch_row();
        $HostCount = $Row[0];
        if($HostCount > 0)
        {
          $HostSpeedIn = round($SpeedIn / $HostCount);
          $HostSpeedOut = round($SpeedOut / $HostCount);
        } else
        {
          $HostSpeedIn = $SpeedIn;
          $HostSpeedOut = $SpeedOut;
        }

        $DbResult2 = $this->Database->select('NetworkDevice', '*', $Filter);
        while($Device = $DbResult2->fetch_assoc())
        {
          $DbResult3 = $this->Database->select('NetworkInterface', '*', '`Device` = '.$Device['Id'].' AND `LocalIP` != ""');
          while($Interface = $DbResult3->fetch_assoc())
          {
            $Name = $Device['Name'];
            if($Interface['Name'] != '') $Name .= '-'.$Interface['Name'];
            $Name = RouterOSIdent($Name);
            echo($Name.', ');
            $PacketMark = GetMarkByComment($Name.'-out');
            $ItemsQueue[] = array('name' => $Name.'-out', 'limit-at' => $HostSpeedIn, 'max-limit' => $UserMaxSpeedIn, 'parent' => $MemberName.'-out', 'packet-mark' => $PacketMark);
            $PacketMark = GetMarkByComment($Name.'-in');
            $ItemsQueue[] = array('name' => $Name.'-in', 'limit-at' => $HostSpeedOut, 'max-limit' => $UserMaxSpeedOut, 'parent' => $MemberName.'-in', 'packet-mark' => $PacketMark);
            $this->CheckName($Name.'-out');
            $this->CheckName($Name.'-in');
          }
        }

        $DbResult2 = $this->Database->select('NetworkSubnet', '*', '`Service`='.$Service['RelId']);
        while($Subnet = $DbResult2->fetch_assoc())
        {
          $Subnet['Name'] = RouterOSIdent('subnet-'.$Subnet['Name']);
          echo($Subnet['Name'].', ');
          $PacketMark = GetMarkByComment($Subnet['Name'].'-out');
          $ItemsQueue[] = array('name' => $Subnet['Name'].'-out', 'limit-at' => $HostSpeedIn, 'max-limit' => $UserMaxSpeedIn, 'parent' => $MemberName.'-out', 'packet-mark' => $PacketMark);
          $PacketMark = GetMarkByComment($Subnet['Name'].'-in');
          $ItemsQueue[] = array('name' => $Subnet['Name'].'-in', 'limit-at' => $HostSpeedOut, 'max-limit' => $UserMaxSpeedOut, 'parent' => $MemberName.'-in', 'packet-mark' => $PacketMark);
          $this->CheckName($Subnet['Name'].'-out');
          $this->CheckName($Subnet['Name'].'-in');
        }
        echo("\n");
        $ServiceIndex++;
      }
    }

    //print_r($ItemsQueue);
    $Routerboard->ListUpdate($PathQueue, array('name', 'limit-at', 'max-limit', 'parent', 'packet-mark'), $ItemsQueue, array(), true);
  }

  function UpdateMinSpeed($DeviceId)
  {
    $MinSpeed = 0;
    foreach($this->Devices[$DeviceId]['Childs'] as $DeviceChild)
    {
      $this->UpdateMinSpeed($DeviceChild);
      $MinSpeed += $this->Devices[$DeviceChild]['MinSpeed'];
    }
    $this->Devices[$DeviceId]['MinSpeed'] = $MinSpeed;
    if($this->Devices[$DeviceId]['DeviceCount'] > 0)
      $this->Devices[$DeviceId]['MinSpeed'] += round($this->Devices[$DeviceId]['InternetSpeedMin'] / $this->Devices[$DeviceId]['DeviceCount']);
  }

  // Calculate maximum real speed available for each network device Start with main router and continue with adjecement nodes.
  function BuildTree($RootDeviceId, $BaseSpeed)
  {
    // Load network devices
    $this->Devices = array();
    $DbResult = $this->Database->query('SELECT `NetworkDevice`.`Name`,`NetworkDevice`.`Id`, '.
      '`Service`.`InternetSpeedMin`, `Service`.`InternetSpeedMax`, '.
      '(SELECT COUNT(*) FROM `NetworkDevice` AS `T` WHERE `T`.`Service` = `NetworkDevice`.`Service`) AS `DeviceCount` FROM `NetworkDevice` '.
      'LEFT JOIN `ServiceCustomerRel` ON `ServiceCustomerRel`.`Id`=`NetworkDevice`.`Service` '.
      'LEFT JOIN `Service` ON `Service`.`Id` = `ServiceCustomerRel`.`Service`');
    while($Device = $DbResult->fetch_assoc())
    {
      $Device['Interfaces'] = array();
      $Device['Calculated'] = false;
      $Device['MaxSpeed'] = 0;
      $Device['MinSpeed'] = 0;
      $Device['Childs'] = array();
      $Device['Parent'] = 0;
      $Device['QueueName'] = '';
      $this->Devices[$Device['Id']] = $Device;
    }

    // Load network interfaces and assign them to device
    $Interfaces = array();
    $DbResult = $this->Database->query('SELECT `Device`,`Name`,`Id` FROM `NetworkInterface`');
    while($Interface = $DbResult->fetch_assoc())
    {
      $Interface['Links'] = array();
      $Interfaces[$Interface['Id']] = $Interface;
      $this->Devices[$Interface['Device']]['Interfaces'][] = $Interface['Id'];
    }

    // Load network links and assign them to interfaces
    $Links = array();
    $DbResult = $this->Database->query('SELECT `NetworkLink`.`Id`,`NetworkLink`.`Interface1`,'.
      '`NetworkLink`.`Interface2`,`NetworkLinkType`.`MaxRealSpeed` FROM `NetworkLink` '.
      'LEFT JOIN `NetworkLinkType` ON `NetworkLinkType`.`Id`=`NetworkLink`.`Type`');
    while($Link = $DbResult->fetch_assoc())
    {
      $Links[$Link['Id']] = $Link;
      $Interfaces[$Link['Interface1']]['Links'][] = $Link['Id'];
      $Interfaces[$Link['Interface2']]['Links'][] = $Link['Id'];
    }

    // Calculate maximum speed for network devices
    $DevicesToCheck = array($RootDeviceId);
    $this->Devices[$RootDeviceId]['MaxSpeed'] = $BaseSpeed;
    $this->Devices[$RootDeviceId]['Calculated'] = true;

    while(count($DevicesToCheck) > 0)
    {
      //echo('Pass'."\n");
      $NewDevicesToCheck = array();
      foreach($DevicesToCheck as $DeviceId)
      {
        //echo($this->Devices[$DeviceId]['Name'].': ');
        foreach($this->Devices[$DeviceId]['Interfaces'] as $InterfaceId)
        {
          foreach($Interfaces[$InterfaceId]['Links'] as $LinkId)
          {
            $Link = $Links[$LinkId];
            $Interface2Id = $Link['Interface1'];
            if($Interface2Id == $InterfaceId) $Interface2Id = $Links[$LinkId]['Interface2'];

            $Device2Id = $Interfaces[$Interface2Id]['Device'];
            if($this->Devices[$Device2Id]['Calculated'] == false)
            {
              $this->Devices[$Device2Id]['Calculated'] = true;
              $NewMaxSpeed = $this->Devices[$DeviceId]['MaxSpeed'];
              if($NewMaxSpeed > $Link['MaxRealSpeed'])
                $NewMaxSpeed = $Link['MaxRealSpeed'];
              //echo($this->Devices[$Device2Id]['Name'].' '.$Device2Id.', ');
              $this->Devices[$Device2Id]['MaxSpeed'] = $NewMaxSpeed;
              // Set nodes tree relation
              $this->Devices[$Device2Id]['Parent'] = $DeviceId;
              $this->Devices[$DeviceId]['Childs'][] = $Device2Id;
              //echo($NewMaxSpeed.", ".count($NewDevicesToCheck).' ');
              $NewDevicesToCheck[] = $Device2Id;
            }
          }
        }
        //echo("\n");
      }
      $DevicesToCheck = $NewDevicesToCheck;
    }

    // Calculate maximum speed for network devices
    $this->UpdateMinSpeed($RootDeviceId);

    //print_r($this->Devices);

    echo('Not linked network devices: ');
    foreach($this->Devices as $Device)
    {
      if($Device['MaxSpeed'] == 0) echo($Device['Name'].', ');
    }
    echo("\n");
  }

  function BuildQueueItems($DeviceId)
  {
    $Device = $this->Devices[$DeviceId];

    // Device
    if($Device['Parent'] != 0)
      $ParentQueueName = $this->Devices[$Device['Parent']]['QueueName'];
      else $ParentQueueName = 'main';
    $Name = $Device['Name'];
    $Name = RouterOSIdent($Name);
    $DeviceName = $Name;
    $this->Devices[$DeviceId]['QueueName'] = $Name;
    echo($Name.', ');
    $PacketMark = GetMarkByComment($Name.'-out');
    $this->ItemsQueue[] = array('name' => $Name.'-out', 'limit-at' => $Device['MinSpeed'],
      'max-limit' => $Device['MaxSpeed'], 'parent' => $ParentQueueName.'-out',
      'packet-mark' => $PacketMark);
    $PacketMark = GetMarkByComment($Name.'-in');
    $this->ItemsQueue[] = array('name' => $Name.'-in', 'limit-at' => $Device['MinSpeed'],
      'max-limit' => $Device['MaxSpeed'], 'parent' => $ParentQueueName.'-in',
      'packet-mark' => $PacketMark);
    $this->CheckName($Name.'-out');
    $this->CheckName($Name.'-in');

    // Interfaces
    $DbResult3 = $this->Database->select('NetworkInterface', '*', '`Device` = '.$DeviceId.' AND `LocalIP` != ""');
    $IntCount = $DbResult3->num_rows;
    while($Interface = $DbResult3->fetch_assoc())
    {
      $Name = $Device['Name'];
      if($Interface['Name'] != '') $Name .= '-'.$Interface['Name'];
        else $Name .= '-';
      $Name = RouterOSIdent($Name);
      echo($Name.', ');
      $PacketMark = GetMarkByComment($Name.'-out');
      $this->ItemsQueue[] = array('name' => $Name.'-out', 'limit-at' => round($Device['MinSpeed'] / $IntCount),
        'max-limit' => $Device['MaxSpeed'], 'parent' => $DeviceName.'-out',
        'packet-mark' => $PacketMark);
      $PacketMark = GetMarkByComment($Name.'-in');
      $this->ItemsQueue[] = array('name' => $Name.'-in', 'limit-at' => round($Device['MinSpeed'] / $IntCount),
        'max-limit' => $Device['MaxSpeed'], 'parent' => $DeviceName.'-in',
        'packet-mark' => $PacketMark);
      $this->CheckName($Name.'-out');
      $this->CheckName($Name.'-in');
    }

    // Process childs
    foreach($Device['Childs'] as $DeviceChild)
    {
      $this->BuildQueueItems($DeviceChild);
    }
  }

  function RunTopology()
  {
    $PathQueue = array('queue', 'tree');

    $Routerboard = new Routerboard();
    $Routerboard->UserName = $this->System->Config['MainRouter']['UserName'];
    $Routerboard->Timeout = $this->System->Config['MainRouter']['ConnectTimeout'];
    $Routerboard->HostName = $this->System->Config['MainRouter']['HostName'];
    $Routerboard->Debug = true;

    $this->UsedNames = array();

    $Finance = &$this->System->Modules['Finance'];
    $Finance->LoadMonthParameters(0);

    $InDivider = 1;
    $OutDivider = 1;
    $TotalMaxSpeedIn = round($Finance->RealMaxSpeed / $InDivider) * 1000;
    $TotalMaxSpeedOut = round($Finance->RealMaxSpeed / $OutDivider) * 1000;
    $UsersMaxSpeedIn = round($Finance->MaxSpeed / $InDivider) * 1000;
    $UsersMaxSpeedOut = round($Finance->MaxSpeed / $OutDivider) * 1000;
    $VoipMaxSpeedIn = $TotalMaxSpeedIn - 136;
    $VoipMaxSpeedOut = $TotalMaxSpeedOut - 136;
    $VoipSpeedIn = 100; //$SpeedReserve;
    $VoipSpeedOut = 100; //$SpeedReserve;
    $OutInterface = 'eth1';
    $InInterface = 'ifb0';
    $InetInterface = $this->System->Config['MainRouter']['InetInterface'];

    $DbResult = $this->Database->select('Service', '*', '(`ChangeAction` IS NULL) AND (`Id`='.TARIFF_FREE.')');
    if($DbResult->num_rows == 1)
    {
      $Service = $DbResult->fetch_array();
      $FreeInetSpeed = $Service['InternetSpeedMax'];
    } else $FreeInetSpeed = 0;

    $this->ItemsQueue = array();

    // Root of tree and main limit
    $this->ItemsQueue[] = array('name' => 'main-out', 'limit-at' => $UsersMaxSpeedIn, 'max-limit' => $UsersMaxSpeedIn, 'parent' => 'global');
    $this->ItemsQueue[] = array('name' => 'main-in', 'limit-at' => $UsersMaxSpeedOut, 'max-limit' => $UsersMaxSpeedOut, 'parent' => 'global');

    // Slow free internet
    $PacketMark = GetMarkByComment('free-out');
    $this->ItemsQueue[] = array('name' => 'free-out', 'limit-at' => $FreeInetSpeed, 'max-limit' => $FreeInetSpeed, 'parent' => 'main-out', 'packet-mark' => $PacketMark);
    $PacketMark = GetMarkByComment('free-in');
    $this->ItemsQueue[] = array('name' => 'free-in', 'limit-at' => $FreeInetSpeed, 'max-limit' => $FreeInetSpeed, 'parent' => 'main-in', 'packet-mark' => $PacketMark);

    $this->BuildTree($this->System->Config['MainRouter']['DeviceId'], 140 * 1000 * 1000);

    $this->BuildQueueItems($this->System->Config['MainRouter']['DeviceId']);

    //print_r($this->ItemsQueue);
    $Routerboard->ListUpdate($PathQueue, array('name', 'limit-at', 'max-limit',
      'parent', 'packet-mark'), $this->ItemsQueue, array(), true);
  }
}
