#todo:
#- defaulty dla x i y jak braqje
#- defulty dla typów elementów - text, title, author, link 
#- inicjalizacja wielkości na początku /splatting
#- możliwość podania położenia jako +x do poprzedniej
#- możliwość użycia align=center, right, left
#- jak brakuje jakiegoś efektu, po ptostu go olac. teraz wywala.

<#
.SYNOPSIS
  PowerPresentation is a PowerShell console-host-based presentation engine.
.DESCRIPTION
  there are a lot of silly stuff we do for fun. or to learn. it's hard to say y can anyone possibly want a presentation engine running in console.
  yet someone made an ASCII art StarWars movie accessible via telnet, so why not having text-presentation?
  PP consists of three elements: this engine, effects library and xml-based presentation file. 
  
  PPE effects file
  
    ppe - PowerPrestation Effects is a ps1 file containing effects fuctions. 
    all functions are dot-sourced during program launch. functions are called by name by putting 'effect="<name>"' tag in xpp file.  

  XPP presentation file
  
    xpp [XML-PowerPresenation] is a simple xml file with following objects:
      level 0: <presentation>
      level 1: <page>
        page has possible parameters: 
          id - just a page ID shown in title bar
          title - title shown in title bar
          effectIn - effect run before page is displayed
          effectOut - effect run on page close
      level 2: <title>,<text>,<author>,<link>
        those are text objects. there are no differences currently between those objects. possible parameters:
          x,y - used to position the text on the screen
          effect - function name processing text
          <other> - there might be other parameters. function should receive entire object [title/text/...] so can parse any additional parameters required.
            
.EXAMPLE
  .\PowerPresentation.ps1 .\presentationFile.xpp
.EXAMPLE
  "somefile.xpp"| .\PowerPresentation.ps1
.PARAMETER presentationFile
  xpp file. 
.NOTES
  author: nExoR 2o14
.LINK
  http://w-files.pl
#>
[CmdletBinding(DefaultParametersetName="Help")]
param(
    [Parameter(Mandatory=$true,Position=0,ParameterSetName="PowerPresentation",valueFromPipeline=$true)][string]$presentationFile,
    [Parameter(Mandatory=$false,Position=1,ParameterSetName="PowerPresentation")][int]$screenSizeX=80,
    [Parameter(Mandatory=$false,Position=2,ParameterSetName="PowerPresentation")][int]$screenSizeY=40,
    [Parameter(Position=0,ParameterSetName="Help")][switch]$help
)

function setPresentationScreen {
  param( [int]$width=80, 
         [int]$height=40,
         [string]$BGColor="white",
         [string]$FGColor="black",
         [string]$title="-o((: PowerPresentation :))o-" 
  )
  $script:startBufforSize=$host.ui.rawui.bufferSize
  $script:startWindowSize=$host.ui.rawui.windowSize
  $script:startBGColor=$host.ui.rawui.BackgroundColor
  $script:startFGColor=$host.ui.rawui.ForegroundColor
  $host.ui.rawui.windowSize=new-object System.Management.Automation.Host.Size($width,$height)
  $host.ui.rawui.bufferSize=new-object System.Management.Automation.Host.Size($width,$height)
  $host.ui.rawui.BackgroundColor=$BGColor
  $host.ui.rawui.ForegroundColor=$FGColor
  $host.ui.rawui.WindowTitle=$title 
  #initialize some script-global variables that might be used in all functions
  $script:pnr=1
  $script:windowHeight=$height
  $script:windowWidth=$width
}
function endPresentation {
  $host.ui.rawui.bufferSize=$script:startBufforSize
  $host.ui.rawui.windowSize=$script:startWindowSize
  $host.ui.rawui.BackgroundColor=$script:startBGColor
  $host.ui.rawui.ForegroundColor=$script:startFGColor 
  cls
  write-host "PowerPresentation ended :))o-"
  exit
} 
function cleanNode {
  param( [System.Xml.XmlElement]$element )
  $host.ui.rawui.cursorposition=New-Object System.Management.Automation.Host.Coordinates 0,$element.y
  $cleanString=" "*$script:windowWidth
  write-host $cleanString
}
function printHeader {
  param( 
    [System.Xml.XmlElement]$pageInfo
  )
  cls
  $pnr=$script:pnr++
  $title="untitled"
  $hbgcolor="darkBlue"
  $hfgcolor="white"
  if($pageInfo.hasAttribute('id')) { $pnr=$pageInfo.id }
  if($pageInfo.hasAttribute('title')) {$title=$pageInfo.getAttribute('title')}
  if($pageInfo.hasAttribute('bgcolor')) {$hbgcolor=$pageInfo.bgcolor}
  if($pageInfo.hasAttribute('fgcolor')) {$hfgcolor=$pageInfo.fgcolor}
  $host.ui.rawui.cursorposition=New-Object System.Management.Automation.Host.Coordinates 0,0
  $text="page $pnr / $title"
  $spaces=" "*($script:windowWidth-$text.length)
  write-host -foregroundColor $hfgcolor -BackgroundColor $hbgcolor -noNewLine "$text$spaces" 
} 

#test parametes and display help if none
if($psboundparameters.count -eq 0 -or $help) { 
  get-help $MyInvocation.MyCommand.Path
  return 
}

$presentationFile=(Get-Item $presentationFile).FullName
try {
    [xml]$presentation=Get-Content $presentationFile
} catch {
    Write-Error "errors found in xpp file"
    return
}

#load effects
get-childItem "$($pwd.path)\*.ppe.ps1"| %{ 
  $effectFile=$_
  try {
    . $effectFile
  } catch {
    write-host "error reading $effectFile effect file. file skipped"
    $key=$host.ui.RawUI.readKey('AllowCtrlC,NoEcho,IncludeKeyUp')
    $host.ui.rawui.flushInputBuffer()
  }
}
setPresentationScreen -title $presentation.presentation.title

#load presentation pages
$pages=select-xml -xml $presentation -xpath '//page'
$currentPage=$pages.node[0]

################################################################################
#                          do the presentation                                 #
################################################################################
while($currentPage -ne $null) {
  printHeader $currentPage
  if($currentPage.hasAttribute('effectIn')) {
    invoke-expression -command ($currentpage.effectIn +' $currentPage')
  }
  $currentNode=$currentPage.firstChild
  $pageY=2 #track current row 
  while($currentNode -ne $null) {
    if(!$currentNode.hasAttribute('x')) {$currentNode.setAttribute('x',"3")}
    if(!$currentNode.hasAttribute('y')) {
      $currentNode.setAttribute('y',"$pageY")
    } else {
      if($pageY -lt [int]($currentNode.y)+2) { $pageY=[int]($currentNode.y)+2 }
    }
    if($currentNode.hasAttribute('effect')) {
      #HERE is where effect function is called
      invoke-expression -command ($currentNode.effect +' $currentNode')
    } else {
      writeTextXY $currentNode 
    }
    if($currentNode.pause) {
      $key=$host.ui.RawUI.readKey('AllowCtrlC,NoEcho,IncludeKeyUp')
      $host.ui.rawui.flushInputBuffer()
      switch($key.VirtualKeyCode) {
        8 {
          cleanNode $currentNode
          $currentNode=$currentNode.PreviousSibling
        }
        27 {endPresentation}
        default {$currentNode=$currentNode.nextSibling}
      }
    } else {
      $currentNode=$currentNode.nextSibling
    }   
  }
  $key=nextPage
  switch($key.VirtualKeyCode) {
    8 {$currentPage=$currentPage.previousSibling}
    27 {endPresentation}
    default {
      if($currentPage.hasAttribute('effectOut')) {
        invoke-expression -command ($currentpage.effectOut +' $currentPage')
      }
      $currentPage=$currentPage.nextSibling
    }
  }
}
endPresentation