#!/usr/bin/perl
# ----------------------------------------------------------------------------
# This software is Copyright (C) 2007, All Rights Reserved
# ----------------------------------------------------------------------------
# This program is protected by local and international copyright laws. Any
# use of this program is subject to the the terms of the license agreement
# included as part of this distribution archive. Any other uses are strictly
# prohibited without the written permission of the Vendor and all
# other rights are reserved.
# -----------------------------------------------------------------------------
# Warning : Modifying this program violates your license agreement!
# -----------------------------------------------------------------------------

package IT::ArtMan::CustomMenus::Publish::CategoryMenus;

# load modules
use strict;

use IT::Globals;

# ------------------------------------------------------------------------------
# Function    : publish
# Description :
# Usage       : &IT::ArtMan::CustomMenus::Publish::CategoryMenus::publish($rule, $publishUpdatedOnly, $stepRef, $subStepRef);
# ------------------------------------------------------------------------------

sub publish {
  my $rule               = shift || die "no rule specified!\n";
  my $publishUpdatedOnly = shift;
  my $stepRef            = shift || die "no currentStepRef defined!\n";
  my $subStepRef         = shift || die "no currentSubStepRef defined!\n";

  # get publish rule type
  my $menuType       = $rule->{'categoryListPublishTopOrCategory'};
  my $isStepComplete = 0;
  if    ($menuType eq 'top')      { $isStepComplete = &publishGlobalMenu($rule, $publishUpdatedOnly, $stepRef, $subStepRef); }
  elsif ($menuType eq 'category') { $isStepComplete = &publishCategoryMenus($rule, $publishUpdatedOnly, $stepRef, $subStepRef); }
  else { die "categoryListPublishTopOrCategory must be 'top' or 'category' not '$menuType'!"; }

  #
  return $isStepComplete;
}

# ------------------------------------------------------------------------------
# Function    : publishGlobalMenu
# Description :
# Usage       : &IT::ArtMan::CustomMenus::Publish::CategoryMenus::publishGlobalMenu($rule, $publishUpdatedOnly, $stepRef, $subStepRef);
# ------------------------------------------------------------------------------

sub publishGlobalMenu {
  my $rule               = shift || die "no rule specified!\n";
  my $publishUpdatedOnly = shift;
  my $stepRef            = shift || die "no currentStepRef defined!\n";
  my $subStepRef         = shift || die "no currentSubStepRef defined!\n";
  my $publishAll         = !$publishUpdatedOnly;

  # error checking
  if ($rule->{'categoryListPublishTopOrCategory'} ne 'top') { die "categoryListPublishTopOrCategory must be 'top'!"; }

  # does rule need publishing? (global rules only publish selected categories, check if any selected categories have been updated)
  my $ruleNeedsPublish    = 0;
  if ($publishAll || $rule->{'alwaysPublish'}) { $ruleNeedsPublish = 1; }
  elsif ($publishUpdatedOnly) {
    my $selectedCategoryNums = &IT::ArtMan::CustomMenus::Admin::PublishRules::getSelectedCategoryNumsForPublishRule($rule);
    my $updatedCategoryNums  = &IT::ArtMan::CustomMenus::Publish::getCategoryNumsWithPublishFlag($selectedCategoryNums, 'needsPublish');
    $ruleNeedsPublish        = (@{$updatedCategoryNums} > 0);
  }

  # display status message
  if ($ruleNeedsPublish) { &IT::MultiStepOperations::addStatusMessage("Publishing: $rule->{'name'} (global $rule->{'type'})\n"); }
  else                   { &IT::MultiStepOperations::addStatusMessage("No updates for: $rule->{'name'} (global $rule->{'type'})\n"); }

  # publish menu
  if ($ruleNeedsPublish) {
    my $currentArticle  = {};
    my $currentCategory = {};
    my $filepath        = &IT::ArtMan::CustomMenus::Publish::getFilePathAndCreateDirFor($rule, $currentCategory, $currentArticle);
    my $html            = &getHtmlForMenu($rule, $currentCategory);
    &IT::File::writeFileFast($filepath, $html);
  }

  #
  my $isStageComplete = 1;
  return $isStageComplete;

}

# ------------------------------------------------------------------------------
# Function    : publishCategoryMenus
# Description :
# Usage       : &IT::ArtMan::CustomMenus::Publish::CategoryMenus::publishCategoryMenus($rule, $publishUpdatedOnly, $stepRef, $subStepRef);
# ------------------------------------------------------------------------------

sub publishCategoryMenus {
  my $rule               = shift || die "no rule specified!\n";
  my $publishUpdatedOnly = shift;
  my $stepRef            = shift || die "no currentStepRef defined!\n";
  my $subStepRef         = shift || die "no currentSubStepRef defined!\n";
  my $publishAll         = !$publishUpdatedOnly;

  # error checking
  if ($rule->{'categoryListPublishTopOrCategory'} ne 'category') { die "categoryListPublishTopOrCategory must be 'category'!"; }

  # does rule need publishing? (category rules publish menus for the selected categories listing ALL categories, check if any category has been updated)
  my $ruleNeedsPublish = 0;
  if ($publishAll || $rule->{'alwaysPublish'}) { $ruleNeedsPublish = 1; }
  elsif ($publishUpdatedOnly) {
    my $allCategoryRecords   = &IT::ArtMan::CustomMenus::Category::getHashOfCategoryRecords();
    my @allCategoryNums      = keys %$allCategoryRecords;
    my $updatedCategoryNums  = &IT::ArtMan::CustomMenus::Publish::getCategoryNumsWithPublishFlag(\@allCategoryNums, 'needsPublish');
    $ruleNeedsPublish        = (@{$updatedCategoryNums} > 0);
  }

  # on first step - display status message
  my $isFirstStep = ($$stepRef == 0);
  if    ($ruleNeedsPublish && $isFirstStep) { &IT::MultiStepOperations::addStatusMessage("Publishing: $rule->{'name'} (category $rule->{'type'})\n"); }
  elsif (!$ruleNeedsPublish)                { &IT::MultiStepOperations::addStatusMessage("No updates for: $rule->{'name'} (category $rule->{'type'})\n"); }

  # on last step
  my $selectedCategoryNums = &IT::ArtMan::CustomMenus::Admin::PublishRules::getSelectedCategoryNumsForPublishRule($rule);
  my $totalSteps           = @$selectedCategoryNums;
  my $noMoreRecords        = !$ruleNeedsPublish || ($$stepRef >= $totalSteps);
  if ($noMoreRecords) {
    return 1;  # stage is complete
  }

  ### Publish next category menu

  my $categoryRecords = &IT::ArtMan::CustomMenus::Category::getHashOfCategoryRecords();
  my $categoryNum     = $selectedCategoryNums->[ $$stepRef ] || die "Couldn't find category num for current step!";
  my $currentCategory = $categoryRecords->{$categoryNum} || die "Couldn't find category record for category num '$categoryNum'!";
  my $currentArticle  = {};
  my $filepath        = &IT::ArtMan::CustomMenus::Publish::getFilePathAndCreateDirFor($rule, $currentCategory, $currentArticle);

  # get html and publish file
  my $isPublished  = $currentCategory->{'type'} ne 'disabled';
  if ($isPublished) {
    &IT::MultiStepOperations::addStatusMessage(" - category: $currentCategory->{'name'}\n");
    my $html = &getHtmlForMenu($rule, $currentCategory);
    &IT::File::writeFileFast($filepath, $html);
  }

  # update stage and step counters
  $$stepRef++;

  # return 0 for stage not complete - return 1 is above when there are no more records
  return 0;
}

# ------------------------------------------------------------------------------
# Function    : getHtmlForMenu
# Description : publish a single category menu
# Usage       : my $html = &IT::ArtMan::CustomMenus::Publish::CategoryMenus::getHtmlForMenu($rule, $currentCategory);
# ------------------------------------------------------------------------------

sub getHtmlForMenu {
  if (@_ != 2) { die "getHtmlForMenu requires 2 arguments!"; }
  my $rule             = shift || die "no rule specified!";
  my $selectedCategory = shift || die "no article records specified";

  # get categories to include
  my $sortedCategories;
  my $isGlobalMenu = (keys %$selectedCategory == 0);
  if ($isGlobalMenu)  { # list selected categories for global menu
    my $sortedCategoryNums = &IT::ArtMan::CustomMenus::Admin::PublishRules::getSelectedCategoryNumsForPublishRule($rule);
    my $categoryRecords    = &IT::ArtMan::CustomMenus::Category::getHashOfCategoryRecords();
    @$sortedCategories     = map { $categoryRecords->{$_} } @$sortedCategoryNums;
    }
  else { # list all categories for "category specific" menus
    $sortedCategories  = &IT::ArtMan::CustomMenus::Category::getSortedCategoryArray();
  }

  # get menu attributes
  my $menuAttributes = &_getMenuAttributesFromRule($rule);

  # generate menu
  my $categoryListHtml;
  my $currentArticle  = {};
  foreach my $index (0..$#$sortedCategories) {
    my $thisCategory       = $sortedCategories->[$index];
    if ($thisCategory->{'type'} eq 'disabled') { next; }  # skip disabled categories
    my $isSelectedCategory = $thisCategory->{'num'} == $selectedCategory->{'num'};
    my $showThisCategory   = &_categoryMatchesFormatRules($menuAttributes, $thisCategory, $selectedCategory, $isSelectedCategory); # only show categories that match a menu attribute rule
    if (!$showThisCategory) { next; } # skip categories not matching format rules
    my $prevCategory       = ($index > 0)                   ? $sortedCategories->[$index-1] : {};
    my $nextCategory       = ($index < $#$sortedCategories) ? $sortedCategories->[$index+1] : {};
    #my $isFirstCategory    = $prevCategory->{'num'} ? 0 : 1;   # consider adding these placeholder in future if needed/requested
    #my $isLastCategory     = $nextCategory->{'num'} ? 0 : 1;
    #my $isBranchStart      = ($isFirstCategory || $prevCategory->{'depth'} < $thisCategory->{'depth'});
    #my $isBranchEnd        = ($isLastCategory || $nextCategory->{'depth'} < $thisCategory->{'depth'});

    # get placeholders
    my $rowPlaceholders = &IT::ArtMan::CustomMenus::Publish::createPlaceholders($thisCategory, $currentArticle, $rule->{'template'}, $rule->{'num'});
    $rowPlaceholders->{'isSelectedCategory'}       = $isSelectedCategory ? 1 : 0;             # deprecated
    $rowPlaceholders->{'category.isSelected'}      = $rowPlaceholders->{'isSelectedCategory'};
    #$rowPlaceholders->{'category.isFirstCategory'} = $isFirstCategory;   # consider adding these placeholder in future if needed/requested
    #$rowPlaceholders->{'category.isLastCategory'}  = $isLastCategory;
    #$rowPlaceholders->{'category.isBranchStart'}   = $isBranchStart ? 1 : 0;
    #$rowPlaceholders->{'category.isBranchEnd'}     = $isBranchEnd ? 1 : 0;
    $rowPlaceholders->{'category.listItemStart'}    = &_getListItemStartPlaceholder($prevCategory, $thisCategory, $nextCategory);
    $rowPlaceholders->{'category.listItemEnd'}      = &_getListItemEndPlaceholder($prevCategory, $thisCategory, $nextCategory);

    # add row
    $categoryListHtml .= &templateCell($rule->{'template'}, 'categoryRow', $rowPlaceholders);
  }

  # define placeholders
  my $placeholders  = &IT::ArtMan::CustomMenus::Publish::createPlaceholders($selectedCategory, $currentArticle, $rule->{'template'}, $rule->{'num'});

  # generate page content
  &IT::Template::addPlaceholderToWhitelist('categoryList');
  $placeholders->{'categoryList'} = $categoryListHtml;
  my $html = &template($rule->{'template'}, $placeholders);

  return $html;
}

# ------------------------------------------------------------------------------
# Function    : _getListItemStartPlaceholder
# Description :
# Usage       : $rowPlaceholders->{'listItemStart'} = &_getListItemStartPlaceholder($prevCategory, $thisCategory, $nextCategory);
# ------------------------------------------------------------------------------

sub _getListItemStartPlaceholder {
  my($prevCategory, $thisCategory, $nextCategory) = @_;
  my $listItemStart = '';

  # start new lists
  if ($prevCategory->{'depth'} < $thisCategory->{'depth'}) {
    for my $depth (reverse $prevCategory->{'depth'}+1 .. $thisCategory->{'depth'}) {
      my $paddingMultiple    = ($depth * 2);
      my $openingListPadding = "  " x $paddingMultiple;
      $listItemStart        .= "\n$openingListPadding<ul>";
    }
  }

  # start this item
  {
    my $paddingMultiple    = ($thisCategory->{'depth'} * 2)+1;
    my $openingItemPadding = "  " x $paddingMultiple;
    $listItemStart        .= "\n$openingItemPadding<li>";
  };

  #
  return $listItemStart;
}

# ------------------------------------------------------------------------------
# Function    : _getListItemEndPlaceholder
# Description :
# Usage       : $rowPlaceholders->{'listItemStart'} = &_getListItemEndPlaceholder($prevCategory, $thisCategory, $nextCategory);
# ------------------------------------------------------------------------------

sub _getListItemEndPlaceholder {
  my($prevCategory, $thisCategory, $nextCategory) = @_;
  my $listItemEnd = '';

    # close this item
    if ($nextCategory->{'depth'} <= $thisCategory->{'depth'}) {
      $listItemEnd .= "</li>";
    }

    # close prev lists and items
    if ($nextCategory->{'depth'} < $thisCategory->{'depth'}) {
      for my $depth (reverse $nextCategory->{'depth'}+1 .. $thisCategory->{'depth'}) {
        my $paddingMultiple    = $depth * 2;
        my $closingListPadding = "  " x ($paddingMultiple);
        my $closingItemPadding = "  " x ($paddingMultiple-1);
        $listItemEnd          .= "\n$closingListPadding</ul>";
        $listItemEnd          .= "\n$closingItemPadding</li>";
      }
    }

  #
  my $isLastCategory = $nextCategory->{'num'} ? 0 : 1;
  if ($isLastCategory) { $listItemEnd .= "\n"; }

  #
  return $listItemEnd;
}

# ------------------------------------------------------------------------------
# Function    : _getMenuAttributesFromRule
# Description :
# Usage       : my $menuAttributes = &_getMenuAttributesFromRule($rule);
# ------------------------------------------------------------------------------

sub _getMenuAttributesFromRule {
  my $rule = shift || die "no rule specified!";

  # get menu attributes
  my @menuAttributes;                                             # rootDepthVisible, showSelectedCategory, childDepthVisible, parentVisibility
  if    ($rule->{'categoryListFormat'} eq 'showAll')            { @menuAttributes = ('all', 1,     'all', 'parentBranches'); }
  elsif ($rule->{'categoryListFormat'} eq 'oneLevel')           { @menuAttributes = (1,     1,     1,     'parentBranches'); }
  elsif ($rule->{'categoryListFormat'} eq 'twoLevel')           { @menuAttributes = (2,     1,     1,     'parentBranches'); }
  elsif ($rule->{'categoryListFormat'} eq 'selectedBranchOnly') { @menuAttributes = (0,     1,     1,     'parentsOnly'); }
  elsif ($rule->{'categoryListFormat'} eq 'subcatsOnly')        { @menuAttributes = (0,     0,     1,     'nothing'); }
  elsif ($rule->{'categoryListFormat'} eq 'breadcrumbs')        { @menuAttributes = (0,     1,     0,     'parentsOnly'); }
  elsif ($rule->{'categoryListFormat'} eq 'custom')             { @menuAttributes = ($rule->{'customMenuDepthVisibleFromRoot'},
                                                                                     $rule->{'customMenuShowSelectedCategory'},
                                                                                     $rule->{'customMenuChildDepthVisible'},
                                                                                     $rule->{'customMenuParentVisibility'}); }
  else { die "unknown categoryListFormat '$rule->{'categoryListFormat'}'\n"; }

  #
  return \@menuAttributes
}

# ------------------------------------------------------------------------------
# Function    : _categoryMatchesFormatRules
# Description :
# Usage       : my $showThisCategory = &_categoryMatchesFormatRules($menuAttributes, $categoryRecord, $selectedCategory, $isSelectedCategory)
# ------------------------------------------------------------------------------

sub _categoryMatchesFormatRules {
  if (@_ != 4) { die "_categoryMatchesFormatRules requires 2 arguments!"; }
  my $menuAttributes     = shift || die "no menu attributes specified!";
  my $categoryRecord     = shift || die "no category record specified!";
  my $selectedCategory   = shift || die "no selected category specified!";
  my $isSelectedCategory = shift;

  # define vars
  my $depthVisibleFromRoot = $menuAttributes->[0];    # always show X level(s) of subcategories from root
  my $showSelectedCategory = $menuAttributes->[1];    # show selected category
  my $childDepthVisible    = $menuAttributes->[2];    # show Y level(s) of subcategories below selected category
  my $parentVisibility     = $menuAttributes->[3];    # show Z above selected categories (nothing, parentsOnly, parentBranches)
  my $showThisCategory     = 0;

  # depth always visible from root
  if ($depthVisibleFromRoot eq 'all')                     { $showThisCategory = 1; }
  if ($categoryRecord->{'depth'} < $depthVisibleFromRoot) { $showThisCategory = 1; }

  # childDepthVisible
  my $isChildOfSelected = $categoryRecord->{'lineage'} =~ /:$selectedCategory->{'num'}:/;
  if ($isChildOfSelected) {
    if ($childDepthVisible eq 'all') { $showThisCategory = 1; }
    if ($categoryRecord->{'depth'} <= ($selectedCategory->{'depth'} + $childDepthVisible)) { $showThisCategory = 1; }
  }

  # parentVisibility
  my %directAncestorsOfSelected = map { $_ => 1 } split(/:/, $selectedCategory->{'lineage'});
  my $isChildOfParentOrAncestor = $directAncestorsOfSelected{ $categoryRecord->{'parentNum'} } ? 1 : 0;

  my $isDirectParentOfSelected = $selectedCategory->{'lineage'} =~ /:$categoryRecord->{'num'}:/;
  if    ($parentVisibility eq 'parentsOnly' && $isDirectParentOfSelected)            { $showThisCategory = 1; }
  elsif ($parentVisibility eq 'parentBranches' && $isChildOfParentOrAncestor) { $showThisCategory = 1; }

  # isSelectedCategory
  if ($isSelectedCategory && !$showSelectedCategory)       { $showThisCategory = 0; }
  ### /only show categories that match a menu attribute rule

  ### hide unlinked categories
  my $isUnlinkedCategory = $categoryRecord->{'type'} eq 'unlinked';
  if ($isUnlinkedCategory) {
    $showThisCategory = 0;
  }

  #
  return $showThisCategory;

}

# ----------------------------------------------------------------------------
# end of file
1;

