import {BitmapImage2D} from "../image/BitmapImage2D";
import {IAsset} from "../library/IAsset";
import {URLRequest} from "../net/URLRequest";
import {AbstractMethodError} from "../errors/AbstractMethodError";
import {AssetEvent} from "../events/AssetEvent";
import {EventDispatcher} from "../events/EventDispatcher";
import {ParserEvent} from "../events/ParserEvent";
import {TimerEvent} from "../events/TimerEvent";
import {ParserUtils} from "../parsers/ParserUtils";
import {ResourceDependency} from "../parsers/ResourceDependency";
import {ByteArray} from "../utils/ByteArray";
import {ImageUtils} from "../utils/ImageUtils";
import {Timer} from "../utils/Timer";
import {getTimer} from "../utils/getTimer";
/**
* ParserBase provides an abstract base export class for objects that convert blocks of data to data structures
* supported by away.
*
* If used by Loader to automatically determine the parser type, two public static methods should
* be implemented, with the following signatures:
*
* public static supportsType(extension : string) : boolean
* Indicates whether or not a given file extension is supported by the parser.
*
* public static supportsData(data : *) : boolean
* Tests whether a data block can be parsed by the parser.
*
* Furthermore, for any concrete subtype, the method initHandle should be overridden to immediately
* create the object that will contain the parsed data. This allows ResourceManager to return an object
* handle regardless of whether the object was loaded or not.
*
* @see Loader
*/
export class ParserBase extends EventDispatcher
{
public _isParsing:boolean;
public _iFileName:string;
private _dataFormat:string;
private _data:any;
private _frameLimit:number;
private _lastFrameTime:number;
private _pOnIntervalDelegate:(event:TimerEvent) => void;
public _pContent:IAsset;
//----------------------------------------------------------------------------------------------------------------------------------------------------------------
// TODO: add error checking for the following ( could cause a problem if this function is not implemented )
//----------------------------------------------------------------------------------------------------------------------------------------------------------------
// Needs to be implemented in all Parsers (
//public static supportsType(extension : string) : boolean
//* Indicates whether or not a given file extension is supported by the parser.
//----------------------------------------------------------------------------------------------------------------------------------------------------------------
public static supportsType(extension:string):boolean
{
throw new AbstractMethodError();
}
private _dependencies:Array;
private _parsingPaused:boolean;
private _parsingComplete:boolean;
private _parsingFailure:boolean;
private _timer:Timer;
private _materialMode:number;
/**
* Returned by proceedParsing to indicate no more parsing is needed.
*/
public static PARSING_DONE:boolean = true;
/* Protected */
/**
* Returned by proceedParsing to indicate more parsing is needed, allowing asynchronous parsing.
*/
public static MORE_TO_PARSE:boolean = false;
/* Protected */
public get content():IAsset
{
return this._pContent;
}
/**
* Creates a new ParserBase object
* @param format The data format of the file data to be parsed. Can be either ParserDataFormat.BINARY or ParserDataFormat.PLAIN_TEXT, and should be provided by the concrete subtype.
*
* @see away.loading.parsers.ParserDataFormat
*/
constructor(format:string)
{
super();
this._materialMode = 0;
this._dataFormat = format;
this._dependencies = new Array();
this._pOnIntervalDelegate = (event:TimerEvent) => this._pOnInterval(event);
}
/**
* Validates a bitmapData loaded before assigning to a default BitmapMaterial
*/
public isBitmapImage2DValid(bitmapImage2D:BitmapImage2D):boolean
{
var isValid:boolean = ImageUtils.isImage2DValid(bitmapImage2D);
if (!isValid) {
console.log(">> Bitmap loaded is not having power of 2 dimensions or is higher than 2048");
}
return isValid;
}
public set parsingFailure(b:boolean)
{
this._parsingFailure = b;
}
public get parsingFailure():boolean
{
return this._parsingFailure;
}
public get parsingPaused():boolean
{
return this._parsingPaused;
}
public get parsingComplete():boolean
{
return this._parsingComplete;
}
public set materialMode(newMaterialMode:number)
{
this._materialMode = newMaterialMode;
}
public get materialMode():number
{
return this._materialMode;
}
public get data():any
{
return this._data;
}
/**
* The data format of the file data to be parsed. Options are URLLoaderDataFormat.BINARY, URLLoaderDataFormat.ARRAY_BUFFER, URLLoaderDataFormat.BLOB, URLLoaderDataFormat.VARIABLES or URLLoaderDataFormat.TEXT.
*/
public get dataFormat():string
{
return this._dataFormat;
}
/**
* Parse data (possibly containing bytearry, plain text or BitmapAsset) asynchronously, meaning that
* the parser will periodically stop parsing so that the AVM may proceed to the
* next frame.
*
* @param data The untyped data object in which the loaded data resides.
* @param frameLimit number of milliseconds of parsing allowed per frame. The
* actual time spent on a frame can exceed this number since time-checks can
* only be performed between logical sections of the parsing procedure.
*/
public parseAsync(data:any, frameLimit:number = 30):void
{
this._data = data;
this._pStartParsing(frameLimit);
}
/**
* A list of dependencies that need to be loaded and resolved for the object being parsed.
*/
public get dependencies():Array
{
return this._dependencies;
}
/**
* Resolve a dependency when it's loaded. For example, a dependency containing an ImageResource would be assigned
* to a Mesh instance as a BitmapMaterial, a scene graph object would be added to its intended parent. The
* dependency should be a member of the dependencies property.
*
* @param resourceDependency The dependency to be resolved.
*/
public _iResolveDependency(resourceDependency:ResourceDependency):void
{
throw new AbstractMethodError();
}
/**
* Resolve a dependency loading failure. Used by parser to eventually provide a default map
*
* @param resourceDependency The dependency to be resolved.
*/
public _iResolveDependencyFailure(resourceDependency:ResourceDependency):void
{
throw new AbstractMethodError();
}
/**
* Resolve a dependency name
*
* @param resourceDependency The dependency to be resolved.
*/
public _iResolveDependencyName(resourceDependency:ResourceDependency, asset:IAsset):string
{
return asset.name;
}
public _iResumeParsing():void
{
this._parsingPaused = false;
if (this._timer)
this._timer.start();
//get started!
if (!this._isParsing)
this._pOnInterval();
}
public _pFinalizeAsset(asset:IAsset, name:string = null):void
{
var type_event:string;
var type_name:string;
if (name != null)
asset.name = name;
// If the asset has no name, give it
// a per-type default name.
if (!asset.name)
asset.name = asset.assetType;
this.dispatchEvent(new AssetEvent(AssetEvent.ASSET_COMPLETE, asset));
}
/**
* Parse the next block of data.
* @return Whether or not more data needs to be parsed. Can be ParserBase.ParserBase.PARSING_DONE or
* ParserBase.ParserBase.MORE_TO_PARSE.
*/
public _pProceedParsing():boolean
{
throw new AbstractMethodError();
}
public _pDieWithError(message:string = 'Unknown parsing error'):void
{
if (this._timer) {
this._timer.removeEventListener(TimerEvent.TIMER, this._pOnIntervalDelegate);
this._timer.stop();
this._timer = null;
}
this.dispatchEvent(new ParserEvent(ParserEvent.PARSE_ERROR, message));
}
public _pAddDependency(id:string, req:URLRequest, retrieveAsRawData:boolean = false, data:any = null, suppressErrorEvents:boolean = false, sub_id:number=0):ResourceDependency
{
var dependency:ResourceDependency = new ResourceDependency(id, req, data, null, this, retrieveAsRawData, suppressErrorEvents, sub_id);
this._dependencies.push(dependency);
return dependency;
}
public _pPauseAndRetrieveDependencies():void
{
this._pPauseParsing();
this.dispatchEvent(new ParserEvent(ParserEvent.READY_FOR_DEPENDENCIES));
}
public _pPauseParsing():void
{
if (this._timer)
this._timer.stop();
this._parsingPaused = true;
}
/**
* Tests whether or not there is still time left for parsing within the maximum allowed time frame per session.
* @return True if there is still time left, false if the maximum allotted time was exceeded and parsing should be interrupted.
*/
public _pHasTime():boolean
{
return ((getTimer() - this._lastFrameTime) < this._frameLimit);
}
/**
* Called when the parsing pause interval has passed and parsing can proceed.
*/
public _pOnInterval(event:TimerEvent = null):void
{
this._lastFrameTime = getTimer();
this._isParsing = true;
if (this._pProceedParsing() && !this._parsingFailure)
this._pFinishParsing();
this._isParsing = false;
}
/**
* Initializes the parsing of data.
* @param frameLimit The maximum duration of a parsing session.
*/
public _pStartParsing(frameLimit:number):void
{
this._frameLimit = frameLimit;
this._timer = new Timer(this._frameLimit, 0);
this._timer.addEventListener(TimerEvent.TIMER, this._pOnIntervalDelegate);
this._timer.start();
//get started!
this._pOnInterval();
}
/**
* Finish parsing the data.
*/
public _pFinishParsing():void
{
if (this._timer) {
this._timer.removeEventListener(TimerEvent.TIMER, this._pOnIntervalDelegate);
this._timer.stop();
}
this._timer = null;
this._parsingComplete = true;
this._isParsing = false;
this.dispatchEvent(new ParserEvent(ParserEvent.PARSE_COMPLETE));
}
/**
*
* @returns {string}
* @private
*/
public _pGetTextData():string
{
return ParserUtils.toString(this._data);
}
/**
*
* @returns {ByteArray}
* @private
*/
public _pGetByteData():ByteArray
{
return ParserUtils.toByteArray(this._data);
}
/**
*
* @returns {any}
* @private
*/
public _pGetData():any
{
return this._data;
}
}