#include <stdio.h>
#include <string.h>

#include "ReportsText.h"
#include "app_common.h"
#include "CoverageRanges.h"
#include "DesiredSymbols.h"
#include "Explanations.h"
#include "ObjdumpProcessor.h"


namespace Coverage {

ReportsText::ReportsText( time_t timestamp ):
  ReportsBase( timestamp )
{
  reportExtension_m = ".txt";
}

ReportsText::~ReportsText()
{
}

void ReportsText::AnnotatedStart(
  FILE*                aFile
)
{
  fprintf( 
    aFile, 
    "========================================"
    "=======================================\n" 
  );
}
 
void ReportsText::AnnotatedEnd(
  FILE*                aFile
)
{
}

void ReportsText::PutAnnotatedLine( 
  FILE*                aFile, 
  AnnotatedLineState_t state, 
  std::string          line, 
  uint32_t             id 
)
{
  fprintf( aFile, "%s\n", line.c_str());
}

bool ReportsText::PutNoBranchInfo(
  FILE*           report
)
{
  if ( BranchInfoAvailable )
    fprintf( report, "All branch paths taken.\n" );
  else
    fprintf( report, "No branch information found.\n" );
  return true;
}


bool ReportsText::PutBranchEntry(
  FILE*   report,
  unsigned int                                     number,
  Coverage::DesiredSymbols::symbolSet_t::iterator  symbolPtr,
  Coverage::CoverageRanges::ranges_t::iterator     rangePtr
)
{
  const Coverage::Explanation* explanation;

  // Add an entry to the report
  fprintf(
    report,
    "============================================\n"
    "Symbol        : %s (0x%x)\n"
    "Line          : %s (0x%x)\n"
    "Size in Bytes : %d\n",
    symbolPtr->first.c_str(),
    symbolPtr->second.baseAddress,
    rangePtr->lowSourceLine.c_str(),
    rangePtr->lowAddress,
    rangePtr->highAddress - rangePtr->lowAddress + 1
  );

  if (rangePtr->reason ==
    Coverage::CoverageRanges::UNCOVERED_REASON_BRANCH_ALWAYS_TAKEN)
    fprintf(
      report, "Reason        : %s\n\n", "ALWAYS TAKEN"
    );
  else if (rangePtr->reason ==
    Coverage::CoverageRanges::UNCOVERED_REASON_BRANCH_NEVER_TAKEN)
    fprintf( report, "Reason        : %s\n\n", "NEVER TAKEN" );

  // See if an explanation is available
  explanation = AllExplanations->lookupExplanation( rangePtr->lowSourceLine );

  if ( !explanation ) {
    fprintf(
      report,
      "Classification: NONE\n"
      "\n"
      "Explanation:\n"
      "No Explanation\n"
    );
  } else {
    fprintf(
      report,
      "Classification: %s\n"
      "\n"
      "Explanation:\n",
      explanation->classification.c_str()
    );

    for ( unsigned int i=0 ;
          i < explanation->explanation.size();
          i++) {
      fprintf(
        report,
        "%s\n",
        explanation->explanation[i].c_str()
      );
    }
  }

  fprintf(
    report, "============================================\n"
  );

  return true;
}

void ReportsText::putCoverageNoRange(
  FILE*         report,
  FILE*         noRangeFile,
  unsigned int  number,
  std::string   symbol
)
{
      fprintf(
        report,
        "============================================\n"
        "Symbol        : %s\n\n"
        "          *** NEVER REFERENCED ***\n\n"
        "This symbol was never referenced by an analyzed executable.\n"
        "Therefore there is no size or disassembly for this symbol.\n"
        "This could be due to symbol misspelling or lack of a test for\n"
        "this symbol.\n"
        "============================================\n",
        symbol.c_str()
      );
      fprintf( noRangeFile, "%s\n", symbol.c_str() );
}

bool ReportsText::PutCoverageLine(
  FILE*                                       report,
  unsigned int                                     number,
  Coverage::DesiredSymbols::symbolSet_t::iterator ditr,
  Coverage::CoverageRanges::ranges_t::iterator    ritr
)
{
  const Coverage::Explanation*   explanation;

  fprintf(
    report,
    "============================================\n"
    "Index                : %d\n"
    "Symbol               : %s (0x%x)\n"
    "Starting Line        : %s (0x%x)\n"
    "Ending Line          : %s (0x%x)\n"
    "Size in Bytes        : %d\n"
    "Size in Instructions : %d\n\n",
    ritr->id,
    ditr->first.c_str(),
    ditr->second.baseAddress,
    ritr->lowSourceLine.c_str(),
    ritr->lowAddress,
    ritr->highSourceLine.c_str(),
    ritr->highAddress,
    ritr->highAddress - ritr->lowAddress + 1,
    ritr->instructionCount
  );

  explanation = AllExplanations->lookupExplanation( ritr->lowSourceLine );

  if ( !explanation ) {
    fprintf(
      report,
      "Classification: NONE\n"
      "\n"
      "Explanation:\n"
      "No Explanation\n"
    );
  } else {
    fprintf(
      report,
      "Classification: %s\n"
      "\n"
      "Explanation:\n",
      explanation->classification.c_str()
    );

    for ( unsigned int i=0; i < explanation->explanation.size(); i++) {
      fprintf( report,"%s\n", explanation->explanation[i].c_str() );
    }
  }

  fprintf(report, "============================================\n");
  return true;
}

bool  ReportsText::PutSizeLine(
  FILE*                                      report,
  unsigned int                                     number,
  Coverage::DesiredSymbols::symbolSet_t::iterator symbol,
  Coverage::CoverageRanges::ranges_t::iterator    range
)
{
  fprintf(
    report,
    "%d\t%s\t%s\n",
    range->highAddress - range->lowAddress + 1,
    symbol->first.c_str(),
    range->lowSourceLine.c_str()
  );
  return true;
}

bool  ReportsText::PutSymbolSummaryLine(
  FILE*                                           report,
  unsigned int                                    number,
  Coverage::DesiredSymbols::symbolSet_t::iterator symbol
)
{
  float uncoveredBytes;
  float uncoveredInstructions;

  if ( symbol->second.stats.sizeInInstructions == 0 )
    uncoveredInstructions = 0;
  else
    uncoveredInstructions = (symbol->second.stats.uncoveredInstructions*100.0)/
                            symbol->second.stats.sizeInInstructions;

  if ( symbol->second.stats.sizeInBytes == 0 )
    uncoveredBytes = 0;
  else
    uncoveredBytes = (symbol->second.stats.uncoveredBytes*100.0)/
                     symbol->second.stats.sizeInBytes;

  fprintf(
    report,
    "============================================\n"
    "Symbol                            : %s\n"
    "Total Size in Bytes               : %d\n"
    "Total Size in Instructions        : %d\n"
    "Total number Branches             : %d\n"
    "Total Always Taken                : %d\n"
    "Total Never Taken                 : %d\n"
    "Percentage Uncovered Instructions : %.2f\n"
    "Percentage Uncovered Bytes        : %.2f\n",
    symbol->first.c_str(),
    symbol->second.stats.sizeInBytes,
    symbol->second.stats.sizeInInstructions,
    symbol->second.stats.branchesNotExecuted +  symbol->second.stats.branchesExecuted,
    symbol->second.stats.branchesAlwaysTaken,
    symbol->second.stats.branchesNeverTaken,
    uncoveredInstructions,
    uncoveredBytes
  );

  fprintf(report, "============================================\n");
  return true;
}

}
