<?php

namespace App\Helpers\Utility;

use App\Models\ProceduralTunnelIp;

class Procedural {
  protected $ip_address;
  
  function __construct() {

  }

  public static function generate_next_v4s($_local, $_remote) {
    $local_octets = explode('.', $_local);
    $remote_octets = explode('.', $_remote);

    // Add n+3 if possible.
    if((int)$remote_octets[3] + 3 < 255) {
      $local_octets[3] += 4;
      $remote_octets[3] += 4;
    }
    else { // If n+3 isn't possible, add one onto one of the prior octets.
      $local_octets[3] = 1;
      $remote_octets[3] = 2;

      if((int)$remote_octets[2] + 1 < 255) {
        ++$local_octets[2];
        ++$remote_octets[2];
      }
      else {
        $local_octets[2] = 0;
        $remote_octets[2] = 0;

        if((int)$remote_octets[1] + 1 < 255) {
          ++$local_octets[1];
          ++$remote_octets[1];
        }
        else {
          $local_octets[1] = 0;
          $remote_octets[1] = 0;

          if((int)$remote_octets[0] + 1 < 255) {
            ++$local_octets[0];
            ++$remote_octets[0];
          } else {
            // If we've reached the end of our range of addresses...
            return false;
          }
        }
      }
    }

      return [ implode('.', $local_octets), implode('.', $remote_octets) ];
  }

  public static function generate_next_v6s($_local, $_remote) {
    $local_raw_hextets = explode(':', $_local);
    $remote_raw_hextets = explode(':', $_remote);

    $local_hextets = [];
    $remote_hextets = [];

    // Convert the hex to decimal so we don't end up making the code unreadable, and so we're not doing it way more times than necessary for conditional checks, etc.
    for($i = 0; $i < 8; ++$i) {
      $local_hextets[$i] = (int)hexdec($local_raw_hextets[$i]);
      $remote_hextets[$i] = (int)hexdec($remote_raw_hextets[$i]);
    }

    if($remote_hextets[3] + 3 < 65535) {
      $local_hextets[7] += 4;
      $remote_hextets[7] += 4;
    }
    else { // If n+3 isn't possible, add one onto one of the prior hextets.
      for($i = 6; $i >= 0; --$i) {
        if($remote_hextets[$i] + 1 < 65535) {
          ++$local_hextets[$i];
          ++$remote_hextets[$i];
        }
        else {
          $local_hextets[$i+1] = 0;
          $remote_hextets[$i+1] = 0;
        }
      }

      $local_hextets[7] = 1;
      $remote_hextets[7] = 2;
    }

    // Convert the hextets back into their raw hexadecimal representation then implode them by their separator.
    for($i = 0; $i < 8; ++$i) {
      $local_hextets[$i] = dechex($local_hextets[$i]);
      $remote_hextets[$i] = dechex($remote_hextets[$i]);
    }

    $cleaned_local = preg_replace('/:0/', ':0000', implode(':', $local_hextets));
    $cleaned_remote = preg_replace('/:0/', ':0000', implode(':', $remote_hextets));

    return [ $cleaned_local, $cleaned_remote ];
  }

  public function generate_tunnel_addresses($_region = 'Global') {
    // Select the last generated address for a given region.
    $ip = ProceduralTunnelIp::where('region', '=', $_region)->orderByDesc('id')->limit(1)->get()[0] ?? null;
    
    // Set up the two initial addresses.
    $local_return = '10.0.0.1';
    $remote_return = '10.0.0.2';

    $localv6_return = '2401:3bc0:0000:0000:0000:0000:4:1';
    $remotev6_return = '2401:3bc0:0000:0000:0000:0000:4:2';

    // If a generated address exists for a given region.
    if($ip != null) {
      if($ip->local != null) {
        [$local_return, $remote_return] = $this::generate_next_v4s($ip->local, $ip->remote);
      }

      if($ip->localv6 != null) {
        [$localv6_return, $remotev6_return] = $this::generate_next_v6s($ip->localv6, $ip->remotev6);
      }
    }

    // Log the address in the database.
    ProceduralTunnelIp::create([
      'region'  => $_region,
      'local' => $local_return,
      'remote' => $remote_return,
      'localv6' => $localv6_return,
      'remotev6' => $remotev6_return
    ]);

    // Return it for the program.
    return [
      'local' => $local_return,
      'remote' => $remote_return,
      'localv6' => $localv6_return,
      'remotev6' => $remotev6_return
    ];
  }

}

?>