/*******************************************************************************
 * Copyright (c) 2008 ARM Limited and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * ARM Limited - Initial API and implementation
 *******************************************************************************/

package org.eclipse.cdt.debug.internal.ui.elements.adapters;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigInteger;
import java.text.MessageFormat;

import org.eclipse.cdt.debug.core.CDebugUtils;
import org.eclipse.cdt.debug.core.model.IDisassemblyInstruction;
import org.eclipse.cdt.debug.core.model.IDisassemblySourceLine;
import org.eclipse.cdt.debug.internal.ui.disassembly.editor.DisassemblyEditorPresentation;
import org.eclipse.cdt.debug.ui.disassembly.IDocumentElementLabelProvider;
import org.eclipse.cdt.debug.ui.disassembly.IDocumentElementLabelUpdate;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.model.ISourceLocator;
import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;

/**
 * org.eclipse.cdt.debug.internal.ui.elements.adapters.DisassemblyElementLabelProvider: 
 * //TODO Add description.
 */
public class DisassemblyElementLabelProvider implements IDocumentElementLabelProvider {

    /* (non-Javadoc)
     * @see org.eclipse.cdt.debug.ui.disassembly.IDocumentElementLabelProvider#update(org.eclipse.cdt.debug.ui.disassembly.IDocumentElementLabelUpdate[])
     */
    public void update( final IDocumentElementLabelUpdate[] updates ) {
        Job job = new Job( "Label update" ) { //$NON-NLS-1$

            /* (non-Javadoc)
             * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
             */
            @Override
            protected IStatus run( IProgressMonitor monitor ) {
                for( int i = 0; i < updates.length; i++ ) {
                    IDocumentElementLabelUpdate update = updates[i];
                    if ( !update.isCanceled() ) {
                        retrieveLabel( update );
                    }
                    update.done();
                }
                return Status.OK_STATUS;
            }
        };
        job.setSystem( true );
        job.schedule();
    }

    protected void retrieveLabel( IDocumentElementLabelUpdate update ) {
        IPresentationContext context = update.getPresentationContext();
        if ( context instanceof DisassemblyEditorPresentation ) {
            DisassemblyEditorPresentation presentation = (DisassemblyEditorPresentation)context;
            Object element = update.getElement();
            if ( element instanceof IDisassemblyInstruction ) {
                IDisassemblyInstruction instruction = (IDisassemblyInstruction)element;
                StringBuilder sb = new StringBuilder();
                if ( presentation.showAddresses() ) {
                    BigInteger address = instruction.getAdress().getValue();
                    sb.append( "0x" ); //$NON-NLS-1$
                    sb.append( CDebugUtils.prependString( Long.toHexString( address.longValue() ), 8, '0' ) );
                    sb.append( '\t' );
                }
                sb.append( instruction.getInstructionText() );
                update.setLabel( DisassemblyEditorPresentation.ATTR_LINE_LABEL, sb.toString() );
            }
            else if ( element instanceof IDisassemblySourceLine ) {
                IDisassemblySourceLine line = (IDisassemblySourceLine)element;
                StringBuilder sb = new StringBuilder();
                if ( presentation.showLineNumbers() ) {
                    sb.append( line.getLineNumber() );
                    sb.append( '\t' );
                }
                sb.append( getSourceLineText( line ) );
                update.setLabel( DisassemblyEditorPresentation.ATTR_LINE_LABEL, sb.toString() );
            }
        }
    }

    private String getSourceLineText( IDisassemblySourceLine line ) {
        File file = line.getFile();
        String text = MessageFormat.format( "File {0} not found.", file.getPath() );
        ISourceLocator locator = line.getDebugTarget().getLaunch().getSourceLocator();
        if ( locator instanceof ISourceLookupDirector ) {
            ISourceLookupDirector director = (ISourceLookupDirector)locator;
            Object sourceElement = director.getSourceElement( file.getPath() );
            if ( sourceElement != null ) {
                File lookupFile = null;
                if ( sourceElement instanceof IFile ) {
                    lookupFile = ((IFile)sourceElement).getLocation().toFile();
                }
                else if ( sourceElement instanceof IStorage ) {
                    lookupFile = ((IStorage)sourceElement).getFullPath().toFile();
                }
                if ( lookupFile != null ) {
                    try {
                        text = readLine( lookupFile, line.getLineNumber() - 1 );
                    }
                    catch( IOException e ) {
                        text = e.getLocalizedMessage();
                    }
                }
            }            
        }
        return text;
    }

    private String readLine( File file, int lineNumber ) throws IOException {
        FileReader fr = new FileReader( file );
        BufferedReader br = new BufferedReader( fr );
        
        try {
            int count = 0;
            String result = null;
            do {
                result = br.readLine();
                if ( count++ == lineNumber )
                    return result;
            } 
            while( result != null );
        }
        finally {
            br.close();
        }
        
        throw new IOException( MessageFormat.format( "Line {0} doesn't exist in {1}.", Integer.valueOf( lineNumber ), file.getPath() ) );
    }
}
