use strict;
use warnings;
use XML::LibXML;
use utf8;
use PPI;
use File::Basename;

sub walk_recursively {
    my ($current_node) = shift;
    my $dom = shift;
    my $parent_xml = shift;
    my $hide_insignificant = shift;
    
    # Get current location
    my $ret = $current_node->location;
    my ($line, $rowchar, $col, $logical_line, $logical_file_name) = @$ret;
    
    # Heredocs are pain in the bum
    if ($current_node->isa('PPI::Token::HereDoc')){
        die("heredocs are BAD!! idk what to do with them");
    }
    
    

    # Skips insignificant tokens (whitespaces)
    if ($hide_insignificant and !$current_node->significant ){  # and !$current_node->isa("PPI::Token::Comment")
        # print STDERR "[$line:$col]"; 
        # print STDERR "Skipping '$current_node'\n";
        return;
    }
    print STDERR "[$line:$col]";
    print STDERR "Current node type: ". ref($current_node);
    print( STDERR " | Token content: ". ($current_node->content eq "\n" ? "<newline>" : $current_node->content)) if $current_node->isa("PPI::Token");
    print STDERR "\n";
    
    #Get it into XML
    my $element_name = ref($current_node);
    if ($current_node->isa("PPI::Document")){
        $element_name = "Document";
    }elsif ($current_node->isa("PPI::Statement")){
        $element_name = "Statement";
    }elsif ($current_node->isa("PPI::Structure")){
        $element_name = "Structure";
    }elsif ($current_node->isa("PPI::Token")){
        $element_name = "Token";
    }

    # Create XML node
    my $current_xml_node = $dom->createElement($element_name);
    $current_xml_node->{"ppi-type"} = ref($current_node);

    if ($current_node->isa("PPI::Statement")){
        $current_xml_node->{"Statement-type"} = (split(/::/, ref($current_node)))[-1];
    }

    if ($current_node->isa("PPI::Structure")){
        if ($current_node->isa("PPI::Structure::Block")){
            $current_xml_node->{"Struct-type"} = "Block";
        }elsif($current_node->isa("PPI::Structure::Subscript")){
            $current_xml_node->{"Struct-type"} = "Subscript";
        }elsif($current_node->isa("PPI::Structure::Constructor")){
            $current_xml_node->{"Struct-type"} = "Constructor";
        }elsif($current_node->isa("PPI::Structure::Condition")){
            $current_xml_node->{"Struct-type"} = "Condition";
        }elsif($current_node->isa("PPI::Structure::List")){
            $current_xml_node->{"Struct-type"} = "List";
        }elsif($current_node->isa("PPI::Structure::For")){
            $current_xml_node->{"Struct-type"} = "For";
        }elsif($current_node->isa("PPI::Structure::Given")){
            $current_xml_node->{"Struct-type"} = "Given";
        }elsif($current_node->isa("PPI::Structure::When")){
            $current_xml_node->{"Struct-type"} = "When";
        }elsif($current_node->isa("PPI::Structure::Unknown")){
            $current_xml_node->{"Struct-type"} = "Unknown";
        }
    }

    if ($current_node->isa("PPI::Token")){
        if($current_node->isa("PPI::Token::Whitespace")){
            $current_xml_node->{"Token-type"} = "Whitespace";
        }elsif($current_node->isa("PPI::Token::Comment")){
            $current_xml_node->{"Token-type"} = "Comment";
        }elsif($current_node->isa("PPI::Token::Pod")){
            $current_xml_node->{"Token-type"} = "Pod";
        }elsif($current_node->isa("PPI::Token::Number")){
            $current_xml_node->{"Token-type"} = "Number";
        }elsif($current_node->isa("PPI::Token::Word")){
            $current_xml_node->{"Token-type"} = "Word";
        }elsif($current_node->isa("PPI::Token::DashedWord")){
            $current_xml_node->{"Token-type"} = "DashedWord";
        }elsif($current_node->isa("PPI::Token::Symbol")){
            $current_xml_node->{"Token-type"} = "Symbol";
        }elsif($current_node->isa("PPI::Token::ArrayIndex")){
            $current_xml_node->{"Token-type"} = "ArrayIndex";
        }elsif($current_node->isa("PPI::Token::Operator")){
            $current_xml_node->{"Token-type"} = "Operator";
        }elsif($current_node->isa("PPI::Token::Quote")){
            $current_xml_node->{"Token-type"} = "Quote";
        }elsif($current_node->isa("PPI::Token::QuoteLike")){
            $current_xml_node->{"Token-type"} = "QuoteLike";
        }elsif($current_node->isa("PPI::Token::Regexp")){
            $current_xml_node->{"Token-type"} = "Regexp";
        }elsif($current_node->isa("PPI::Token::HereDoc")){
            $current_xml_node->{"Token-type"} = "HereDoc";
        }elsif($current_node->isa("PPI::Token::Cast")){
            $current_xml_node->{"Token-type"} = "Cast";
        }elsif($current_node->isa("PPI::Token::Structure")){
            $current_xml_node->{"Token-type"} = "Structure";
        }elsif($current_node->isa("PPI::Token::Label")){
            $current_xml_node->{"Token-type"} = "Label";
        }elsif($current_node->isa("PPI::Token::Separator")){
            $current_xml_node->{"Token-type"} = "Separator";
        }elsif($current_node->isa("PPI::Token::Data")){
            $current_xml_node->{"Token-type"} = "Data";
        }elsif($current_node->isa("PPI::Token::End")){
            $current_xml_node->{"Token-type"} = "End";
        }elsif($current_node->isa("PPI::Token::Prototype")){
            $current_xml_node->{"Token-type"} = "Prototype";
        }elsif($current_node->isa("PPI::Token::Attribute")){
            $current_xml_node->{"Token-type"} = "Attribute";
        }elsif($current_node->isa("PPI::Token::Unknown")){
            $current_xml_node->{"Token-type"} = "Unknown";
        }
    }


    # Tokens get some special treatment
    if ($current_node->isa("PPI::Token")){
        $current_xml_node->{line} = $line;
        $current_xml_node->{col} = $col;

        if($current_node->content() eq "\n"){
            $current_xml_node->appendChild($dom->createElement("newline"));
        }else{
            my $content = $current_node->content;
            chomp($content);
            $current_xml_node->appendText($content);
        }
    }

    # Append children
    if ($current_node->isa("PPI::Node")){
        for my $child_node ($current_node->children()){
            walk_recursively($child_node, $dom, $current_xml_node, $hide_insignificant);
        }
    }

    $parent_xml->appendChild($current_xml_node);
    return;
}


while (my $file = shift) {
    my ($filename, $filepath, $filesuffix) = fileparse($file, qr/\.[^.]*/);
    # Create PDOM and index locations
    my $Document = PPI::Document->new($file);
    $Document->index_locations();

    my $xml_dom = XML::LibXML::Document->new('1.0', 'UTF-8');
    my $xml_root = $xml_dom->createElement('program');
    $xml_root->{'file'} = $file;
    $xml_dom->setDocumentElement($xml_root);

    # Here call tree walker recursively
    walk_recursively($Document, $xml_dom, $xml_root, 1);

    # print $xml_dom->toString(1);

    # Save the PDOM
    my $out_file = $filepath.$filename.".pdom";
    print STDERR "Printing PDOM to file $out_file\n";
    $xml_dom->toFile($out_file, 1);
}