/******************************************************************************
 *
 * $Id: $
 *
 *
 * Copyright (C) 1997-2006 by Dimitri van Heesch.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation under the terms of the GNU General Public License is hereby 
 * granted. No representations are made about the suitability of this software 
 * for any purpose. It is provided "as is" without express or implied warranty.
 * See the GNU General Public License for more details.
 *
 */

/*! \mainpage Metrics
 *  This is a small example that shows how to use doxygen's XML output and
 *  the doxmlparser library. The example shows some very basic code metrics.
 */

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

bool isDocumented(IDocRoot *brief,IDocRoot *detailed)
{
  bool found=false;
  if (brief)
  {
    IDocIterator *docIt = brief->contents();
    if (docIt->current()) // method has brief description
    {
      found=true;
    }
    docIt->release();
  }
  if (detailed && !found)
  {
    IDocIterator *docIt = detailed->contents();
    if (docIt->current())
    {
      found=true;
    }
    docIt->release();
  }
  return found;
}

int main(int argc,char **argv)
{
  if (argc!=2)
  {
    printf("Usage: %s xml_output_dir\n",argv[0]);
    exit(1);
  }

  int numClasses=0;
  int numDocClasses=0;
  int numStructs=0;
  int numUnions=0;
  int numInterfaces=0;
  int numExceptions=0;
  int numNamespaces=0;
  int numFiles=0;
  int numGroups=0;
  int numPages=0;
  int numPackages=0;
  int numPubMethods=0;
  int numProMethods=0;
  int numPriMethods=0;
  int numDocPubMethods=0;
  int numDocProMethods=0;
  int numDocPriMethods=0;
  int numFunctions=0;
  int numAttributes=0;
  int numVariables=0;
  int numDocFunctions=0;
  int numDocAttributes=0;
  int numDocVariables=0;
  int numParams=0;
  
  IDoxygen *dox = createObjectModel();

  dox->setDebugLevel(0);

  if (!dox->readXMLDir(argv[1]))
  {
    printf("Error reading %s/index.xml\n",argv[1]);
    exit(1);
  }

  ICompoundIterator *cli = dox->compounds();
  ICompound *comp;
  for (cli->toFirst();(comp=cli->current());cli->toNext())
  {
    printf("Processing %s...\n",comp->name()->latin1());
    bool hasDocs = isDocumented(comp->briefDescription(),comp->detailedDescription());
    switch (comp->kind())
    {
      case ICompound::Class:      
        numClasses++;    
        if (hasDocs) numDocClasses++;
        break;
      case ICompound::Struct:     numStructs++;    break;
      case ICompound::Union:      numUnions++;     break;
      case ICompound::Interface:  numInterfaces++; break;
      case ICompound::Exception:  numExceptions++; break;
      case ICompound::Namespace:  numNamespaces++; break;
      case ICompound::File:       numFiles++;      break;
      case ICompound::Group:      numGroups++;     break;
      case ICompound::Page:       numPages++;      break;
      default: break;
    }
    
    ISectionIterator *sli = comp->sections();
    ISection *sec;
    for (sli->toFirst();(sec=sli->current());sli->toNext())
    {
      IMemberIterator *mli = sec->members();
      IMember *mem;
      for (mli->toFirst();(mem=mli->current());mli->toNext())
      {
        IParamIterator *pli = mem->parameters();
        IParam *par;
        if (comp->kind()==ICompound::Class || 
            comp->kind()==ICompound::Struct ||
            comp->kind()==ICompound::Interface
           )
        {
          if (mem->kind()==IMember::Function ||
              mem->kind()==IMember::Prototype ||
              mem->kind()==IMember::Signal ||
              mem->kind()==IMember::Slot ||
              mem->kind()==IMember::DCOP
             ) // is a "method"
          {
            if (mem->section()->isPublic())
            {
              numPubMethods++;
              if (isDocumented(mem->briefDescription(),mem->detailedDescription()))
              {
                numDocPubMethods++;
              }
            }
            else if (mem->section()->isProtected())
            {
              numProMethods++;
              if (isDocumented(mem->briefDescription(),mem->detailedDescription()))
              {
                numDocProMethods++;
              }
            }
            else if (mem->section()->isPrivate())
            {
              numPriMethods++;
              if (isDocumented(mem->briefDescription(),mem->detailedDescription()))
              {
                numDocPriMethods++;
              }
            }
          }
          else if (mem->kind()==IMember::Variable || 
                   mem->kind()==IMember::Property
                  ) // is an "attribute"
          {
            numAttributes++;
            if (isDocumented(mem->briefDescription(),mem->detailedDescription()))
            {
              numDocAttributes++;
            }
          }
        }
        else if (comp->kind()==ICompound::File ||
                 comp->kind()==ICompound::Namespace
                )
        {
          if (mem->kind()==IMember::Function ||
              mem->kind()==IMember::Prototype ||
              mem->kind()==IMember::Signal ||
              mem->kind()==IMember::Slot ||
              mem->kind()==IMember::DCOP
             ) // is a "method"
          {
            numFunctions++;
            if (isDocumented(mem->briefDescription(),mem->detailedDescription()))
            {
              numDocFunctions++;
            }
          }
          else if (mem->kind()==IMember::Variable || 
                   mem->kind()==IMember::Property
                  ) // is an "attribute"
          {
            numVariables++;
            if (isDocumented(mem->briefDescription(),mem->detailedDescription()))
            {
              numDocVariables++;
            }
          }
        }
        
        for (pli->toFirst();(par=pli->current());pli->toNext())
        {
          numParams++;
        }
        const char *type = mem->typeString()->latin1();
        if (type && strcmp(type, "void"))
        {
          numParams++; // count non-void return types as well
        }
        pli->release();
      }
      mli->release();
    }
    sli->release();

    comp->release();
  }
  cli->release();

  dox->release();

  int numMethods    = numPubMethods+numProMethods+numPriMethods;
  int numDocMethods = numDocPubMethods+numDocProMethods+numDocPriMethods;
  
  printf("Metrics:\n");
  printf("-----------------------------------\n");
  if (numClasses>0)    printf("Classes:     %10d (%d documented)\n",numClasses,numDocClasses);
  if (numStructs>0)    printf("Structs:     %10d\n",numStructs);
  if (numUnions>0)     printf("Unions:      %10d\n",numUnions);
  if (numInterfaces>0) printf("Interfaces:  %10d\n",numInterfaces);
  if (numExceptions>0) printf("Exceptions:  %10d\n",numExceptions);
  if (numNamespaces>0) printf("Namespaces:  %10d\n",numNamespaces);
  if (numFiles>0)      printf("Files:       %10d\n",numFiles);
  if (numGroups>0)     printf("Groups:      %10d\n",numGroups);
  if (numPages>0)      printf("Pages:       %10d\n",numPages);
  if (numPackages>0)   printf("Packages:    %10d\n",numPackages);
  if (numMethods>0)    printf("Methods:     %10d (%d documented)\n",numMethods,numDocMethods);
  if (numPubMethods>0) printf("  Public:    %10d (%d documented)\n",numPubMethods,numDocPubMethods);
  if (numProMethods>0) printf("  Protected: %10d (%d documented)\n",numProMethods,numDocProMethods);
  if (numPriMethods>0) printf("  Private:   %10d (%d documented)\n",numPriMethods,numDocPriMethods);
  if (numFunctions>0)  printf("Functions:   %10d (%d documented)\n",numFunctions,numDocFunctions);
  if (numAttributes>0) printf("Attributes:  %10d (%d documented)\n",numAttributes,numDocAttributes);
  if (numVariables>0)  printf("Variables:   %10d (%d documented)\n",numVariables,numDocVariables);
  if (numParams>0)     printf("Params:      %10d\n",numParams);
  printf("-----------------------------------\n");
  if (numClasses>0)    printf("Avg. #methods/compound:  %10f\n",(double)numMethods/(double)numClasses);
  if (numMethods>0)    printf("Avg. #params/method:     %10f\n",(double)numParams/(double)numMethods);
  printf("-----------------------------------\n");

  return 0;
}

