//////////////////////////////
// Import Parser Pieces
//////////////////////////////
@import "parsers/query";
@import "parsers/single";
@import "parsers/double";
@import "parsers/triple";
@import "parsers/resolution";

$Memo-Exists: function-exists(memo-get) and function-exists(memo-set);

//////////////////////////////
// Breakpoint Function
//////////////////////////////
@function breakpoint($query, $contexts...) {
  $run: true;
  $return: ();

  // Grab the Memo Output if Memoization can be a thing
  @if $Memo-Exists {
    $return: memo-get(breakpoint, breakpoint $query $contexts);

    @if $return != null {
      $run: false;
    }
  }

  @if not $Memo-Exists or $run {
    // Internal Variables
    $query-string: '';
    $query-fallback: false;
    $return: ();

    // Reserve Global Private Breakpoint Context
    $holder-context: $private-breakpoint-context-holder;
    $holder-query-count: $private-breakpoint-query-count;

    // Reset Global Private Breakpoint Context
    $private-breakpoint-context-holder: () !global;
    $private-breakpoint-query-count: 0 !global;


    // Test to see if it's a comma-separated list
    $or-list: if(list-separator($query) == 'comma', true, false);


    @if ($or-list == false and breakpoint-get('legacy syntax') == false) {
      $query-string: breakpoint-parse($query);
    }
    @else {
      $length: length($query);

      $last: nth($query, $length);
      $query-fallback: breakpoint-no-query($last);

      @if ($query-fallback != false) {
        $length: $length - 1;
      }

      @if (breakpoint-get('legacy syntax') == true) {
        $mq: ();

        @for $i from 1 through $length {
          $mq: append($mq, nth($query, $i), comma);
        }

        $query-string: breakpoint-parse($mq);
      }
      @else {
        $query-string: '';
        @for $i from 1 through $length {
          $query-string: $query-string + if($i == 1, '', ', ') + breakpoint-parse(nth($query, $i));
        }
      }
    }

    $return: ('query': $query-string,
        'fallback': $query-fallback,
        'context holder': $private-breakpoint-context-holder,
        'query count': $private-breakpoint-query-count
    );
    @if length($contexts) > 0 and nth($contexts, 1) != false {
      @if $query-fallback != false {
        $context-setter: private-breakpoint-set-context('no-query', $query-fallback);
      }
      $context-map: ();
      @each $context in $contexts {
        $context-map: map-merge($context-map, ($context: breakpoint-get-context($context)));
      }
      $return: map-merge($return, (context: $context-map));
    }

    // Reset Global Private Breakpoint Context
    $private-breakpoint-context-holder: () !global;
    $private-breakpoint-query-count: 0 !global;

    @if $Memo-Exists {
      $holder: memo-set(breakpoint, breakpoint $query $contexts, $return);
    }
  }

  @return $return;
}

//////////////////////////////
// General Breakpoint Parser
//////////////////////////////
@function breakpoint-parse($query) {
  // Increase number of 'and' queries
  $private-breakpoint-query-count: $private-breakpoint-query-count + 1 !global;

  // Set up Media Type
  $query-print: '';

  $force-all: ((breakpoint-get('force all media type') == true) and (breakpoint-get('default media') == 'all'));
  $empty-media: true;
  @if ($force-all == true) or (breakpoint-get('default media') != 'all') {
    // Force the print of the default media type if (force all is true and default media type is all) or (default media type is not all)
    $query-print: breakpoint-get('default media');
    $empty-media: false;
  }


  $query-resolution: false;

  $query-holder: breakpoint-parse-query($query);



  // Loop over each parsed out query and write it to $query-print
  $first: true;

  @each $feature in $query-holder {
    $length: length($feature);

    // Parse a single feature
    @if ($length == 1) {
      // Feature is currently a list, grab the actual value
      $feature: nth($feature, 1);

      // Media Type must by convention be the first item, so it's safe to flat override $query-print, which right now should only be the default media type
      @if (breakpoint-is-media($feature)) {
        @if ($force-all == true) or ($feature != 'all') {
          // Force the print of the default media type if (force all is true and default media type is all) or (default media type is not all)
          $query-print: $feature;
          $empty-media: false;

          // Set Context
          $context-setter: private-breakpoint-set-context(media, $query-print);
        }
      }
      @else {
        $parsed: breakpoint-parse-single($feature, $empty-media, $first);
        $query-print: '#{$query-print} #{$parsed}';
        $first: false;
      }
    }
    // Parse a double feature
    @else if ($length == 2) {
      @if (breakpoint-is-resolution($feature) != false) {
        $query-resolution: $feature;
      }
      @else {
        $parsed: null;
        // If it's a string/number pair,
        // we check to see if one is a single-string value,
        // then we parse it as a normal double
        $alpha: nth($feature, 1);
        $beta: nth($feature, 2);
        @if breakpoint-single-string($alpha) or breakpoint-single-string($beta) {
          $parsed: breakpoint-parse-single($alpha, $empty-media, $first);
          $query-print: '#{$query-print} #{$parsed}';
          $first: false;
          $parsed: breakpoint-parse-single($beta, $empty-media, $first);
          $query-print: '#{$query-print} #{$parsed}';
        }
        @else {
          $parsed: breakpoint-parse-double($feature, $empty-media, $first);
          $query-print: '#{$query-print} #{$parsed}';
          $first: false;
        }
      }
    }
    // Parse a triple feature
    @else if ($length == 3) {
      $parsed: breakpoint-parse-triple($feature, $empty-media, $first);
      $query-print: '#{$query-print} #{$parsed}';
      $first: false;
    }

  }

  @if ($query-resolution != false) {
    $query-print: breakpoint-build-resolution($query-print, $query-resolution, $empty-media, $first);
  }

  // Loop through each feature that's been detected so far and append 'false' to the the value list to increment their counters
  @each $f, $v in $private-breakpoint-context-holder {
    $v-holder: $v;
    $length: length($v-holder);
    @if length($v-holder) < $private-breakpoint-query-count {
      @for $i from $length to $private-breakpoint-query-count {
        @if $f == 'media' {
          $v-holder: append($v-holder, breakpoint-get('default media'));
        }
        @else {
          $v-holder: append($v-holder, false);
        }
      }
    }
    $private-breakpoint-context-holder: map-merge($private-breakpoint-context-holder, ($f: $v-holder)) !global;
  }

  @return $query-print;
}
